From bb6e091027eee3c88b855fbf67775be10f00c16f Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Wed, 3 Feb 2016 15:15:58 +0100 Subject: [PATCH] Switch parameters to proto3, propagate to all code, offer support in all languages --- ...dVehicleRoutingProblemWithTimeWindows.java | 12 +- .../ortools/samples/RabbitsPheasants.java | 12 +- examples/com/google/ortools/samples/Tsp.java | 12 +- examples/cpp/cvrptw.cc | 223 +- examples/cpp/cvrptw_lib.cc | 226 ++ examples/cpp/cvrptw_lib.h | 131 ++ examples/cpp/cvrptw_with_refueling.cc | 207 +- examples/cpp/cvrptw_with_resources.cc | 195 +- .../cvrptw_with_stop_times_and_resources.cc | 222 +- examples/cpp/model_util.cc | 82 +- examples/cpp/nqueens.cc | 6 +- examples/cpp/pdptw.cc | 15 +- examples/csharp/cscvrptw.cs | 10 +- examples/csharp/cstsp.cs | 8 +- examples/python/rabbit_pheasant.py | 7 +- examples/python/tsp.py | 18 +- makefiles/Makefile.cpp.mk | 72 +- makefiles/Makefile.csharp.mk | 65 +- makefiles/Makefile.java.mk | 149 +- makefiles/Makefile.python.mk | 20 +- makefiles/Makefile.third_party.unix | 12 +- makefiles/Makefile.unix | 2 + src/base/file.cc | 12 + src/base/file.h | 4 +- src/com/google/ortools/util/ProtoHelper.cs | 30 + src/constraint_solver/assignment.cc | 36 +- src/constraint_solver/assignment.proto | 58 +- src/constraint_solver/constraint_solver.cc | 114 +- src/constraint_solver/constraint_solver.h | 140 +- src/constraint_solver/constraint_solveri.h | 2 +- .../csharp/constraint_solver.swig | 30 +- src/constraint_solver/csharp/routing.swig | 22 +- src/constraint_solver/demon_profiler.cc | 6 +- src/constraint_solver/demon_profiler.proto | 10 +- src/constraint_solver/expr_array.cc | 16 +- src/constraint_solver/io.cc | 546 ++--- .../java/constraint_solver.swig | 44 +- src/constraint_solver/java/routing.swig | 29 +- src/constraint_solver/local_search.cc | 2 - src/constraint_solver/model.proto | 122 +- .../python/constraint_solver.swig | 115 +- src/constraint_solver/python/routing.swig | 31 +- src/constraint_solver/routing.cc | 2062 ++++++++++------- src/constraint_solver/routing.h | 558 +++-- src/constraint_solver/routing_enums.proto | 111 + src/constraint_solver/routing_flags.cc | 200 ++ src/constraint_solver/routing_flags.h | 77 + .../routing_parameters.proto | 265 +++ src/constraint_solver/routing_search.cc | 274 ++- src/constraint_solver/sched_constraints.cc | 2 +- src/constraint_solver/search.cc | 13 +- src/constraint_solver/search_limit.proto | 21 +- src/constraint_solver/solver_parameters.proto | 95 + src/linear_solver/python/linear_solver.swig | 108 +- .../linear_solver_natural_api.py | 210 +- src/util/csharp/proto.swig | 69 +- src/util/java/proto.swig | 23 +- src/util/java/vector.swig | 6 +- src/util/python/proto.swig | 103 + tools/or-tools.nuspec | 4 +- tools/setup.py | 2 +- tools/setup_data.py | 2 +- tools/setup_py3.py | 2 +- 63 files changed, 4407 insertions(+), 2875 deletions(-) create mode 100644 examples/cpp/cvrptw_lib.cc create mode 100644 examples/cpp/cvrptw_lib.h create mode 100644 src/com/google/ortools/util/ProtoHelper.cs create mode 100644 src/constraint_solver/routing_enums.proto create mode 100644 src/constraint_solver/routing_flags.cc create mode 100644 src/constraint_solver/routing_flags.h create mode 100644 src/constraint_solver/routing_parameters.proto create mode 100644 src/constraint_solver/solver_parameters.proto create mode 100644 src/util/python/proto.swig diff --git a/examples/com/google/ortools/samples/CapacitatedVehicleRoutingProblemWithTimeWindows.java b/examples/com/google/ortools/samples/CapacitatedVehicleRoutingProblemWithTimeWindows.java index d8956a336c..a26ebe2974 100644 --- a/examples/com/google/ortools/samples/CapacitatedVehicleRoutingProblemWithTimeWindows.java +++ b/examples/com/google/ortools/samples/CapacitatedVehicleRoutingProblemWithTimeWindows.java @@ -19,6 +19,7 @@ import com.google.ortools.constraintsolver.Assignment; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.NodeEvaluator2; import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingEnums.FirstSolutionStrategy; import com.google.ortools.constraintsolver.RoutingSearchParameters; import java.util.ArrayList; @@ -229,13 +230,14 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { } // Solving - RoutingSearchParameters parameters = new RoutingSearchParameters(); - parameters.setNo_lns(true); - parameters.setFirst_solution("AllUnperformed"); - parameters.setTrace(true); + RoutingSearchParameters parameters = + RoutingSearchParameters.newBuilder() + .mergeFrom(RoutingModel.defaultSearchParameters()) + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.ALL_UNPERFORMED) + .build(); logger.info("Search"); - Assignment solution = model.solveWithParameters(parameters, null); + Assignment solution = model.solveWithParameters(parameters); if (solution != null) { String output = "Total cost: " + solution.objectiveValue() + "\n"; diff --git a/examples/com/google/ortools/samples/RabbitsPheasants.java b/examples/com/google/ortools/samples/RabbitsPheasants.java index 70e492f6e3..b55414d230 100644 --- a/examples/com/google/ortools/samples/RabbitsPheasants.java +++ b/examples/com/google/ortools/samples/RabbitsPheasants.java @@ -14,6 +14,7 @@ package com.google.ortools.samples; +import com.google.ortools.constraintsolver.ConstraintSolverParameters; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; @@ -39,8 +40,13 @@ public class RabbitsPheasants { * and 56 legs. How many rabbits and how many pheasants are we thus * seeing? */ - private static void solve() { - Solver solver = new Solver("RabbitsPheasants"); + private static void solve(boolean traceSearch) { + ConstraintSolverParameters parameters = + ConstraintSolverParameters.newBuilder() + .mergeFrom(Solver.defaultSolverParameters()) + .setTraceSearch(traceSearch) + .build(); + Solver solver = new Solver("RabbitsPheasants", parameters); IntVar rabbits = solver.makeIntVar(0, 100, "rabbits"); IntVar pheasants = solver.makeIntVar(0, 100, "pheasants"); solver.addConstraint(solver.makeEquality(solver.makeSum(rabbits, pheasants), @@ -59,6 +65,6 @@ public class RabbitsPheasants { } public static void main(String[] args) throws Exception { - RabbitsPheasants.solve(); + RabbitsPheasants.solve(args.length > 0); } } diff --git a/examples/com/google/ortools/samples/Tsp.java b/examples/com/google/ortools/samples/Tsp.java index c19bac5c80..9ccf7836e8 100644 --- a/examples/com/google/ortools/samples/Tsp.java +++ b/examples/com/google/ortools/samples/Tsp.java @@ -23,6 +23,8 @@ import java.text.*; import com.google.ortools.constraintsolver.Assignment; import com.google.ortools.constraintsolver.NodeEvaluator2; import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingSearchParameters; class Tsp { @@ -61,8 +63,6 @@ class Tsp { static void solve(int size, int forbidden, int seed) { RoutingModel routing = new RoutingModel(size, 1); - // Setting first solution heuristic (cheapest addition). - routing.setFirstSolutionStrategy(RoutingModel.ROUTING_PATH_CHEAPEST_ARC); // Setting the cost function. // Put a permanent callback to the distance accessor here. The callback @@ -93,7 +93,13 @@ class Tsp { "dummy"); // Solve, returns a solution if any (owned by RoutingModel). - Assignment solution = routing.solve(); + RoutingSearchParameters search_parameters = + RoutingSearchParameters.newBuilder() + .mergeFrom(RoutingModel.defaultSearchParameters()) + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + + Assignment solution = routing.solveWithParameters(search_parameters); if (solution != null) { // Solution cost. System.out.println("Cost = " + solution.objectiveValue()); diff --git a/examples/cpp/cvrptw.cc b/examples/cpp/cvrptw.cc index 75674e8b00..73d19ea510 100644 --- a/examples/cpp/cvrptw.cc +++ b/examples/cpp/cvrptw.cc @@ -21,8 +21,6 @@ // distances are computed using the Manhattan distance. Distances are assumed // to be in meters and times in seconds. -#include -#include #include #include "base/callback.h" @@ -30,22 +28,19 @@ #include "base/commandlineflags.h" #include "base/integral_types.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "constraint_solver/routing.h" -#include "base/random.h" +#include "constraint_solver/routing_flags.h" +#include "cpp/cvrptw_lib.h" -using operations_research::Assignment; -using operations_research::IntVar; -using operations_research::RoutingDimension; using operations_research::RoutingModel; -using operations_research::Solver; +using operations_research::RoutingSearchParameters; +using operations_research::LocationContainer; +using operations_research::RandomDemand; +using operations_research::ServiceTimePlusTransition; +using operations_research::GetSeed; using operations_research::ACMRandom; -using operations_research::StringAppendF; -using operations_research::StringPrintf; -DECLARE_string(routing_first_solution); -DECLARE_bool(routing_no_lns); 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, @@ -55,183 +50,9 @@ DEFINE_bool(vrp_use_same_vehicle_costs, false, const char* kTime = "Time"; const char* kCapacity = "Capacity"; -const int kMaxNodesPerGroup = 10; +const int64 kMaxNodesPerGroup = 10; const int64 kSameVehicleCost = 1000; -// Random seed generator. -int32 GetSeed() { - if (FLAGS_vrp_use_deterministic_random_seed) { - return ACMRandom::DeterministicSeed(); - } else { - return ACMRandom::HostnamePidTimeSeed(); - } -} - -// Location container, contains positions of orders and can be used to obtain -// Manhattan distances/times between locations. -class LocationContainer { - public: - explicit LocationContainer(int64 speed) - : randomizer_(GetSeed()), speed_(speed) { - CHECK_LT(0, speed_); - } - void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); } - void AddRandomLocation(int64 x_max, int64 y_max) { - AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1)); - } - int64 ManhattanDistance(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return locations_[from].DistanceTo(locations_[to]); - } - int64 ManhattanTime(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return ManhattanDistance(from, to) / speed_; - } - - private: - class Location { - public: - Location() : x_(0), y_(0) {} - Location(int64 x, int64 y) : x_(x), y_(y) {} - int64 DistanceTo(const Location& location) const { - return Abs(x_ - location.x_) + Abs(y_ - location.y_); - } - - private: - static int64 Abs(int64 value) { return std::max(value, -value); } - - int64 x_; - int64 y_; - }; - - ACMRandom randomizer_; - const int64 speed_; - ITIVector locations_; -}; - -// Random demand. -class RandomDemand { - public: - RandomDemand(int size, RoutingModel::NodeIndex depot) - : size_(size), depot_(depot) { - CHECK_LT(0, size_); - } - void Initialize() { - const int64 kDemandMax = 5; - const int64 kDemandMin = 1; - demand_.reset(new int64[size_]); - ACMRandom randomizer(GetSeed()); - for (int order = 0; order < size_; ++order) { - if (order == depot_) { - demand_[order] = 0; - } else { - demand_[order] = - kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1); - } - } - } - int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const { - return demand_[from.value()]; - } - - private: - std::unique_ptr demand_; - const int size_; - const RoutingModel::NodeIndex depot_; -}; - -// Service time (proportional to demand) + transition time callback. -class ServiceTimePlusTransition { - public: - ServiceTimePlusTransition(int64 time_per_demand_unit, - RoutingModel::NodeEvaluator2* demand, - RoutingModel::NodeEvaluator2* transition_time) - : time_per_demand_unit_(time_per_demand_unit), - demand_(demand), - transition_time_(transition_time) {} - int64 Compute(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return time_per_demand_unit_ * demand_->Run(from, to) + - transition_time_->Run(from, to); - } - - private: - const int64 time_per_demand_unit_; - std::unique_ptr demand_; - std::unique_ptr transition_time_; -}; - -// Route plan displayer. -// TODO(user): Move the display code to the routing library. -void DisplayPlan(const RoutingModel& routing, const Assignment& plan) { - // Display plan cost. - std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue()); - - // Display dropped orders. - std::string dropped; - for (int order = 1; order < routing.nodes(); ++order) { - if (plan.Value(routing.NextVar(order)) == order) { - if (dropped.empty()) { - StringAppendF(&dropped, " %d", order); - } else { - StringAppendF(&dropped, ", %d", order); - } - } - } - if (!dropped.empty()) { - 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); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); - for (int route_number = 0; route_number < routing.vehicles(); - ++route_number) { - int64 order = routing.Start(route_number); - StringAppendF(&plan_output, "Route %d: ", route_number); - if (routing.IsEnd(plan.Value(routing.NextVar(order)))) { - plan_output += "Empty\n"; - } else { - while (true) { - IntVar* const load_var = capacity_dimension.CumulVar(order); - IntVar* const time_var = time_dimension.CumulVar(order); - StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld) -> ", - order, plan.Value(load_var), plan.Min(time_var), - plan.Max(time_var)); - if (routing.IsEnd(order)) break; - order = plan.Value(routing.NextVar(order)); - } - plan_output += "\n"; - } - } - LOG(INFO) << plan_output; -} - int main(int argc, char** argv) { gflags::ParseCommandLineFlags( &argc, &argv, true); CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; @@ -242,16 +63,17 @@ int main(int argc, char** argv) { const RoutingModel::NodeIndex kDepot(0); RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles); routing.SetDepot(kDepot); - // Setting first solution heuristic (cheapest addition). - FLAGS_routing_first_solution = "PathCheapestArc"; - // Disabling Large Neighborhood Search, comment out to activate it. - FLAGS_routing_no_lns = true; + RoutingSearchParameters parameters = + operations_research::BuildSearchParametersFromFlags(); + parameters.set_first_solution_strategy( + operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); + parameters.mutable_local_search_operators()->set_use_path_lns(false); // Setting up locations. const int64 kXMax = 100000; const int64 kYMax = 100000; const int64 kSpeed = 10; - LocationContainer locations(kSpeed); + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); for (int location = 0; location <= FLAGS_vrp_orders; ++location) { locations.AddRandomLocation(kXMax, kYMax); } @@ -263,7 +85,8 @@ int main(int argc, char** argv) { // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot); + RandomDemand demand(routing.nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), kNullCapacitySlack, kVehicleCapacity, @@ -278,9 +101,11 @@ int main(int argc, char** argv) { routing.AddDimension( NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); + const operations_research::RoutingDimension& time_dimension = + routing.GetDimensionOrDie(kTime); + // Adding time windows. - ACMRandom randomizer(GetSeed()); + ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; for (int order = 1; order < routing.nodes(); ++order) { const int64 start = randomizer.Uniform(kHorizon - kTWDuration); @@ -313,9 +138,13 @@ int main(int argc, char** argv) { } // Solve, returns a solution if any (owned by RoutingModel). - const Assignment* solution = routing.Solve(); + const operations_research::Assignment* solution = + routing.SolveWithParameters(parameters); if (solution != NULL) { - DisplayPlan(routing, *solution); + DisplayPlan(routing, *solution, FLAGS_vrp_use_same_vehicle_costs, + kMaxNodesPerGroup, kSameVehicleCost, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); } else { LOG(INFO) << "No solution found."; } diff --git a/examples/cpp/cvrptw_lib.cc b/examples/cpp/cvrptw_lib.cc new file mode 100644 index 0000000000..1fc54b103d --- /dev/null +++ b/examples/cpp/cvrptw_lib.cc @@ -0,0 +1,226 @@ +// 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. + +#include "cpp/cvrptw_lib.h" + +#include + +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/routing.h" +#include "base/random.h" +#include "base/random.h" + +namespace operations_research { + +int32 GetSeed(bool deterministic) { + if (deterministic) { + return ACMRandom::DeterministicSeed(); + } else { + return ACMRandom::HostnamePidTimeSeed(); + } +} + +LocationContainer::LocationContainer(int64 speed, bool use_deterministic_seed) + : randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) { + CHECK_LT(0, speed_); +} + +void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max) { + AddRandomLocation(x_max, y_max, 1); +} + +void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max, + int duplicates) { + const int64 x = randomizer_.Uniform(x_max + 1); + const int64 y = randomizer_.Uniform(y_max + 1); + for (int i = 0; i < duplicates; ++i) { + AddLocation(x, y); + } +} + +int64 LocationContainer::ManhattanDistance(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex to) const { + return locations_[from].DistanceTo(locations_[to]); +} + +int64 LocationContainer::NegManhattanDistance( + RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const { + return -ManhattanDistance(from, to); +} + +int64 LocationContainer::ManhattanTime(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex to) const { + return ManhattanDistance(from, to) / speed_; +} + +bool LocationContainer::SameLocation(RoutingModel::NodeIndex node1, + RoutingModel::NodeIndex node2) const { + if (node1 < locations_.size() && node2 < locations_.size()) { + return locations_[node1].IsAtSameLocation(locations_[node2]); + } + return false; +} +int64 LocationContainer::SameLocationFromIndex(int64 node1, int64 node2) const { + // The direct conversion from constraint model indices to routing model + // nodes is correct because the depot is node 0. + // TODO(user): Fetch proper indices from routing model. + return SameLocation(RoutingModel::NodeIndex(node1), + RoutingModel::NodeIndex(node2)); +} + +LocationContainer::Location::Location() : x_(0), y_(0) {} + +LocationContainer::Location::Location(int64 x, int64 y) : x_(x), y_(y) {} + +int64 LocationContainer::Location::DistanceTo(const Location& location) const { + return Abs(x_ - location.x_) + Abs(y_ - location.y_); +} + +bool LocationContainer::Location::IsAtSameLocation( + const Location& location) const { + return x_ == location.x_ && y_ == location.y_; +} + +int64 LocationContainer::Location::Abs(int64 value) { + return std::max(value, -value); +} + +RandomDemand::RandomDemand(int size, RoutingModel::NodeIndex depot, + bool use_deterministic_seed) + : size_(size), + depot_(depot), + use_deterministic_seed_(use_deterministic_seed) { + CHECK_LT(0, size_); +} + +void RandomDemand::Initialize() { + const int64 kDemandMax = 5; + const int64 kDemandMin = 1; + demand_.reset(new int64[size_]); + MTRandom randomizer(GetSeed(use_deterministic_seed_)); + for (int order = 0; order < size_; ++order) { + if (order == depot_) { + demand_[order] = 0; + } else { + demand_[order] = + kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1); + } + } +} + +int64 RandomDemand::Demand(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex /*to*/) const { + return demand_[from.value()]; +} + +ServiceTimePlusTransition::ServiceTimePlusTransition( + int64 time_per_demand_unit, RoutingModel::NodeEvaluator2* demand, + RoutingModel::NodeEvaluator2* transition_time) + : time_per_demand_unit_(time_per_demand_unit), + demand_(demand), + transition_time_(transition_time) {} + +int64 ServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex to) const { + return time_per_demand_unit_ * demand_->Run(from, to) + + transition_time_->Run(from, to); +} + +StopServiceTimePlusTransition::StopServiceTimePlusTransition( + int64 stop_time, const LocationContainer& location_container, + RoutingModel::NodeEvaluator2* transition_time) + : stop_time_(stop_time), + location_container_(location_container), + transition_time_(transition_time) {} + +int64 StopServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex to) const { + return location_container_.SameLocation(from, to) + ? 0 + : stop_time_ + transition_time_->Run(from, to); +} + +void DisplayPlan( + const RoutingModel& routing, const operations_research::Assignment& plan, + bool use_same_vehicle_costs, int64 max_nodes_per_group, + int64 same_vehicle_cost, + const operations_research::RoutingDimension& capacity_dimension, + const operations_research::RoutingDimension& time_dimension) { + // Display plan cost. + std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue()); + + // Display dropped orders. + std::string dropped; + for (int order = 1; order < routing.nodes(); ++order) { + if (plan.Value(routing.NextVar(order)) == order) { + if (dropped.empty()) { + StringAppendF(&dropped, " %d", order); + } else { + StringAppendF(&dropped, ", %d", order); + } + } + } + if (!dropped.empty()) { + plan_output += "Dropped orders:" + dropped + "\n"; + } + + if (use_same_vehicle_costs) { + int group_size = 0; + int64 group_same_vehicle_cost = 0; + std::set visited; + 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 == max_nodes_per_group) { + if (visited.size() > 1) { + group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost; + } + group_size = 0; + visited.clear(); + } + } + if (visited.size() > 1) { + group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost; + } + LOG(INFO) << "Same vehicle costs: " << group_same_vehicle_cost; + } + + // Display actual output for each vehicle. + for (int route_number = 0; route_number < routing.vehicles(); + ++route_number) { + int64 order = routing.Start(route_number); + StringAppendF(&plan_output, "Route %d: ", route_number); + if (routing.IsEnd(plan.Value(routing.NextVar(order)))) { + plan_output += "Empty\n"; + } else { + while (true) { + operations_research::IntVar* const load_var = + capacity_dimension.CumulVar(order); + operations_research::IntVar* const time_var = + time_dimension.CumulVar(order); + StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld) -> ", + order, plan.Value(load_var), plan.Min(time_var), + plan.Max(time_var)); + if (routing.IsEnd(order)) break; + order = plan.Value(routing.NextVar(order)); + } + plan_output += "\n"; + } + } + LOG(INFO) << plan_output; +} +} // namespace operations_research diff --git a/examples/cpp/cvrptw_lib.h b/examples/cpp/cvrptw_lib.h new file mode 100644 index 0000000000..4a0ba86b35 --- /dev/null +++ b/examples/cpp/cvrptw_lib.h @@ -0,0 +1,131 @@ +// 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 header provides functions to help creating random instaces of the +// vehicle routing problem; random capacities and random time windows. +#ifndef OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ +#define OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ + +#include + +#include "constraint_solver/routing.h" +#include "base/random.h" + +namespace operations_research { + +// Random seed generator. +int32 GetSeed(bool deterministic); + +// Location container, contains positions of orders and can be used to obtain +// Manhattan distances/times between locations. +class LocationContainer { + public: + LocationContainer(int64 speed, bool use_deterministic_seed); + void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); } + void AddRandomLocation(int64 x_max, int64 y_max); + void AddRandomLocation(int64 x_max, int64 y_max, int duplicates); + int64 ManhattanDistance( + operations_research::RoutingModel::NodeIndex from, + operations_research::RoutingModel::NodeIndex to) const; + int64 NegManhattanDistance( + operations_research::RoutingModel::NodeIndex from, + operations_research::RoutingModel::NodeIndex to) const; + int64 ManhattanTime(operations_research::RoutingModel::NodeIndex from, + operations_research::RoutingModel::NodeIndex to) const; + + bool SameLocation(operations_research::RoutingModel::NodeIndex node1, + operations_research::RoutingModel::NodeIndex node2) const; + int64 SameLocationFromIndex(int64 node1, int64 node2) const; + + private: + class Location { + public: + Location(); + Location(int64 x, int64 y); + int64 DistanceTo(const Location& location) const; + bool IsAtSameLocation(const Location& location) const; + + private: + static int64 Abs(int64 value); + + int64 x_; + int64 y_; + }; + + MTRandom randomizer_; + const int64 speed_; + ITIVector locations_; +}; + +// Random demand. +class RandomDemand { + public: + RandomDemand(int size, operations_research::RoutingModel::NodeIndex depot, + bool use_deterministic_seed); + void Initialize(); + int64 Demand(operations_research::RoutingModel::NodeIndex from, + operations_research::RoutingModel::NodeIndex to) const; + + private: + std::unique_ptr demand_; + const int size_; + const operations_research::RoutingModel::NodeIndex depot_; + const bool use_deterministic_seed_; +}; + +// Service time (proportional to demand) + transition time callback. +class ServiceTimePlusTransition { + public: + ServiceTimePlusTransition( + int64 time_per_demand_unit, + operations_research::RoutingModel::NodeEvaluator2* demand, + operations_research::RoutingModel::NodeEvaluator2* transition_time); + int64 Compute(operations_research::RoutingModel::NodeIndex from, + operations_research::RoutingModel::NodeIndex to) const; + + private: + const int64 time_per_demand_unit_; + std::unique_ptr demand_; + std::unique_ptr + transition_time_; +}; + +// Stop service time + transition time callback. +class StopServiceTimePlusTransition { + public: + StopServiceTimePlusTransition( + int64 stop_time, const LocationContainer& location_container, + operations_research::RoutingModel::NodeEvaluator2* transition_time); + int64 Compute(operations_research::RoutingModel::NodeIndex from, + operations_research::RoutingModel::NodeIndex to) const; + + private: + const int64 stop_time_; + const LocationContainer& location_container_; + std::unique_ptr demand_; + std::unique_ptr + transition_time_; +}; + +// Route plan displayer. +// TODO(user): Move the display code to the routing library. +void DisplayPlan( + const operations_research::RoutingModel& routing, + const operations_research::Assignment& plan, bool use_same_vehicle_costs, + int64 max_nodes_per_group, int64 same_vehicle_cost, + const operations_research::RoutingDimension& capacity_dimension, + const operations_research::RoutingDimension& time_dimension); + +} // namespace operations_research + +#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ diff --git a/examples/cpp/cvrptw_with_refueling.cc b/examples/cpp/cvrptw_with_refueling.cc index 5b7f2c9885..de822daefc 100644 --- a/examples/cpp/cvrptw_with_refueling.cc +++ b/examples/cpp/cvrptw_with_refueling.cc @@ -19,7 +19,6 @@ // must visit certain nodes (refueling nodes) before the quantity of fuel // reaches zero. Fuel consumption is proportional to the distance traveled. -#include #include #include "base/callback.h" @@ -27,22 +26,22 @@ #include "base/commandlineflags.h" #include "base/integral_types.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "constraint_solver/routing.h" +#include "constraint_solver/routing_flags.h" +#include "cpp/cvrptw_lib.h" #include "base/random.h" -using operations_research::Assignment; -using operations_research::IntVar; -using operations_research::RoutingDimension; +using operations_research::GetSeed; +using operations_research::LocationContainer; +using operations_research::RandomDemand; using operations_research::RoutingModel; -using operations_research::Solver; +using operations_research::RoutingSearchParameters; +using operations_research::ServiceTimePlusTransition; using operations_research::ACMRandom; using operations_research::StringAppendF; using operations_research::StringPrintf; -DECLARE_string(routing_first_solution); -DECLARE_bool(routing_no_lns); 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, @@ -52,174 +51,12 @@ const char* kTime = "Time"; const char* kCapacity = "Capacity"; const char* kFuel = "Fuel"; -// Random seed generator. -int32 GetSeed() { - if (FLAGS_vrp_use_deterministic_random_seed) { - return ACMRandom::DeterministicSeed(); - } else { - return ACMRandom::HostnamePidTimeSeed(); - } -} - // Returns true if node is a refueling node (based on node / refuel node ratio). bool IsRefuelNode(int64 node) { const int64 kRefuelNodeRatio = 10; return (node % kRefuelNodeRatio == 0); } -// Location container, contains positions of orders and can be used to obtain -// Manhattan distances/times between locations. -class LocationContainer { - public: - explicit LocationContainer(int64 speed) - : randomizer_(GetSeed()), speed_(speed) { - CHECK_LT(0, speed_); - } - void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); } - void AddRandomLocation(int64 x_max, int64 y_max) { - AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1)); - } - int64 ManhattanDistance(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return locations_[from].DistanceTo(locations_[to]); - } - int64 NegManhattanDistance(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return -ManhattanDistance(from, to); - } - int64 ManhattanTime(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return ManhattanDistance(from, to) / speed_; - } - - private: - class Location { - public: - Location() : x_(0), y_(0) {} - Location(int64 x, int64 y) : x_(x), y_(y) {} - int64 DistanceTo(const Location& location) const { - return Abs(x_ - location.x_) + Abs(y_ - location.y_); - } - - private: - static int64 Abs(int64 value) { return std::max(value, -value); } - - int64 x_; - int64 y_; - }; - - ACMRandom randomizer_; - const int64 speed_; - ITIVector locations_; -}; - -// Random demand. -class RandomDemand { - public: - RandomDemand(int size, RoutingModel::NodeIndex depot) - : size_(size), depot_(depot) { - CHECK_LT(0, size_); - } - void Initialize() { - const int64 kDemandMax = 5; - const int64 kDemandMin = 1; - demand_.reset(new int64[size_]); - ACMRandom randomizer(GetSeed()); - for (int order = 0; order < size_; ++order) { - if (order == depot_) { - demand_[order] = 0; - } else { - demand_[order] = - kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1); - } - } - } - int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const { - // Refuel nodes don't have a demand. - if (!IsRefuelNode(from.value())) { - return demand_[from.value()]; - } - return 0; - } - - private: - std::unique_ptr demand_; - const int size_; - const RoutingModel::NodeIndex depot_; -}; - -// Service time (proportional to demand) + transition time callback. -class ServiceTimePlusTransition { - public: - ServiceTimePlusTransition(int64 time_per_demand_unit, - RoutingModel::NodeEvaluator2* demand, - RoutingModel::NodeEvaluator2* transition_time) - : time_per_demand_unit_(time_per_demand_unit), - demand_(demand), - transition_time_(transition_time) {} - int64 Compute(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return time_per_demand_unit_ * demand_->Run(from, to) + - transition_time_->Run(from, to); - } - - private: - const int64 time_per_demand_unit_; - std::unique_ptr demand_; - std::unique_ptr transition_time_; -}; - -// Route plan displayer. -// TODO(user): Move the display code to the routing library. -void DisplayPlan(const RoutingModel& routing, const Assignment& plan) { - // Display plan cost. - std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue()); - - // Display dropped orders. - std::string dropped; - for (int order = 1; order < routing.nodes(); ++order) { - if (plan.Value(routing.NextVar(order)) == order) { - if (dropped.empty()) { - StringAppendF(&dropped, " %d", order); - } else { - StringAppendF(&dropped, ", %d", order); - } - } - } - if (!dropped.empty()) { - plan_output += "Dropped orders:" + dropped + "\n"; - } - - // Display actual output for each vehicle. - const RoutingDimension& capacity_dimension = - routing.GetDimensionOrDie(kCapacity); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); - const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel); - for (int route_number = 0; route_number < routing.vehicles(); - ++route_number) { - int64 order = routing.Start(route_number); - StringAppendF(&plan_output, "Route %d: ", route_number); - if (routing.IsEnd(plan.Value(routing.NextVar(order)))) { - plan_output += "Empty\n"; - } else { - while (true) { - IntVar* const load_var = capacity_dimension.CumulVar(order); - IntVar* const time_var = time_dimension.CumulVar(order); - IntVar* const fuel_var = fuel_dimension.CumulVar(order); - StringAppendF(&plan_output, - "%lld Load(%lld) Time(%lld, %lld) Fuel(%lld, %lld) -> ", - order, plan.Value(load_var), plan.Min(time_var), - plan.Max(time_var), plan.Min(fuel_var), - plan.Max(fuel_var)); - if (routing.IsEnd(order)) break; - order = plan.Value(routing.NextVar(order)); - } - } - plan_output += "\n"; - } - LOG(INFO) << plan_output; -} - int main(int argc, char** argv) { gflags::ParseCommandLineFlags( &argc, &argv, true); CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; @@ -230,16 +67,17 @@ int main(int argc, char** argv) { const RoutingModel::NodeIndex kDepot(0); RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles); routing.SetDepot(kDepot); - // Setting first solution heuristic (cheapest addition). - FLAGS_routing_first_solution = "PathCheapestArc"; - // Disabling Large Neighborhood Search, comment out to activate it. - FLAGS_routing_no_lns = true; + RoutingSearchParameters parameters = + operations_research::BuildSearchParametersFromFlags(); + parameters.set_first_solution_strategy( + operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); + parameters.mutable_local_search_operators()->set_use_path_lns(false); // Setting up locations. const int64 kXMax = 100000; const int64 kYMax = 100000; const int64 kSpeed = 10; - LocationContainer locations(kSpeed); + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); for (int location = 0; location <= FLAGS_vrp_orders; ++location) { locations.AddRandomLocation(kXMax, kYMax); } @@ -251,7 +89,8 @@ int main(int argc, char** argv) { // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot); + RandomDemand demand(routing.nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), kNullCapacitySlack, kVehicleCapacity, @@ -266,9 +105,10 @@ int main(int argc, char** argv) { routing.AddDimension( NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); + const operations_research::RoutingDimension& time_dimension = + routing.GetDimensionOrDie(kTime); // Adding time windows. - ACMRandom randomizer(GetSeed()); + ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; for (int order = 1; order < routing.nodes(); ++order) { if (!IsRefuelNode(order)) { @@ -285,7 +125,8 @@ int main(int argc, char** argv) { NewPermanentCallback(&locations, &LocationContainer::NegManhattanDistance), kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, kFuel); - const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel); + const operations_research::RoutingDimension& fuel_dimension = + routing.GetDimensionOrDie(kFuel); for (int order = 0; order < routing.Size(); ++order) { // Only let slack free for refueling nodes. if (!IsRefuelNode(order) || routing.IsStart(order)) { @@ -305,9 +146,13 @@ int main(int argc, char** argv) { } // Solve, returns a solution if any (owned by RoutingModel). - const Assignment* solution = routing.Solve(); + const operations_research::Assignment* solution = + routing.SolveWithParameters(parameters); if (solution != NULL) { - DisplayPlan(routing, *solution); + DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false, + /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); } else { LOG(INFO) << "No solution found."; } diff --git a/examples/cpp/cvrptw_with_resources.cc b/examples/cpp/cvrptw_with_resources.cc index 8c3e4cb4fe..1e197ead7b 100644 --- a/examples/cpp/cvrptw_with_resources.cc +++ b/examples/cpp/cvrptw_with_resources.cc @@ -21,7 +21,6 @@ // empty routes; fix this when we have an API on the cumulative constraints // with variable demands. -#include #include #include "base/callback.h" @@ -29,22 +28,21 @@ #include "base/commandlineflags.h" #include "base/integral_types.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "constraint_solver/routing.h" +#include "constraint_solver/routing_flags.h" +#include "cpp/cvrptw_lib.h" #include "base/random.h" -using operations_research::Assignment; -using operations_research::IntervalVar; -using operations_research::IntVar; -using operations_research::RoutingDimension; using operations_research::RoutingModel; -using operations_research::Solver; +using operations_research::RoutingSearchParameters; +using operations_research::LocationContainer; +using operations_research::RandomDemand; +using operations_research::ServiceTimePlusTransition; +using operations_research::GetSeed; using operations_research::ACMRandom; using operations_research::StringAppendF; using operations_research::StringPrintf; -DECLARE_string(routing_first_solution); -DECLARE_bool(routing_no_lns); 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, @@ -53,155 +51,6 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false, const char* kTime = "Time"; const char* kCapacity = "Capacity"; -// Random seed generator. -int32 GetSeed() { - if (FLAGS_vrp_use_deterministic_random_seed) { - return ACMRandom::DeterministicSeed(); - } else { - return ACMRandom::HostnamePidTimeSeed(); - } -} - -// Location container, contains positions of orders and can be used to obtain -// Manhattan distances/times between locations. -class LocationContainer { - public: - explicit LocationContainer(int64 speed) - : randomizer_(GetSeed()), speed_(speed) { - CHECK_LT(0, speed_); - } - void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); } - void AddRandomLocation(int64 x_max, int64 y_max) { - AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1)); - } - int64 ManhattanDistance(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return locations_[from].DistanceTo(locations_[to]); - } - int64 ManhattanTime(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return ManhattanDistance(from, to) / speed_; - } - - private: - class Location { - public: - Location() : x_(0), y_(0) {} - Location(int64 x, int64 y) : x_(x), y_(y) {} - int64 DistanceTo(const Location& location) const { - return Abs(x_ - location.x_) + Abs(y_ - location.y_); - } - - private: - static int64 Abs(int64 value) { return std::max(value, -value); } - - int64 x_; - int64 y_; - }; - - ACMRandom randomizer_; - const int64 speed_; - ITIVector locations_; -}; - -// Random demand. -class RandomDemand { - public: - RandomDemand(int size, RoutingModel::NodeIndex depot) - : size_(size), depot_(depot) { - CHECK_LT(0, size_); - } - void Initialize() { - const int64 kDemandMax = 5; - const int64 kDemandMin = 1; - demand_.reset(new int64[size_]); - ACMRandom randomizer(GetSeed()); - for (int order = 0; order < size_; ++order) { - if (order == depot_) { - demand_[order] = 0; - } else { - demand_[order] = - kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1); - } - } - } - int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const { - return demand_[from.value()]; - } - - private: - std::unique_ptr demand_; - const int size_; - const RoutingModel::NodeIndex depot_; -}; - -// Service time (proportional to demand) + transition time callback. -class ServiceTimePlusTransition { - public: - ServiceTimePlusTransition(int64 time_per_demand_unit, - RoutingModel::NodeEvaluator2* demand, - RoutingModel::NodeEvaluator2* transition_time) - : time_per_demand_unit_(time_per_demand_unit), - demand_(demand), - transition_time_(transition_time) {} - int64 Compute(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return time_per_demand_unit_ * demand_->Run(from, to) + - transition_time_->Run(from, to); - } - - private: - const int64 time_per_demand_unit_; - std::unique_ptr demand_; - std::unique_ptr transition_time_; -}; - -// Route plan displayer. -// TODO(user): Move the display code to the routing library. -void DisplayPlan(const RoutingModel& routing, const Assignment& plan) { - // Display plan cost. - std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue()); - - // Display dropped orders. - std::string dropped; - for (int order = 1; order < routing.nodes(); ++order) { - if (plan.Value(routing.NextVar(order)) == order) { - if (dropped.empty()) { - StringAppendF(&dropped, " %d", order); - } else { - StringAppendF(&dropped, ", %d", order); - } - } - } - if (!dropped.empty()) { - plan_output += "Dropped orders:" + dropped + "\n"; - } - - // Display actual output for each vehicle. - const RoutingDimension& capacity_dimension = - routing.GetDimensionOrDie(kCapacity); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); - for (int route_number = 0; route_number < routing.vehicles(); - ++route_number) { - int64 order = routing.Start(route_number); - StringAppendF(&plan_output, "Route %d: ", route_number); - if (routing.IsEnd(plan.Value(routing.NextVar(order)))) { - plan_output += "Empty\n"; - } else { - while (true) { - IntVar* const load_var = capacity_dimension.CumulVar(order); - IntVar* const time_var = time_dimension.CumulVar(order); - StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld) -> ", - order, plan.Value(load_var), plan.Min(time_var), - plan.Max(time_var)); - if (routing.IsEnd(order)) break; - order = plan.Value(routing.NextVar(order)); - } - } - } - LOG(INFO) << plan_output; -} - int main(int argc, char** argv) { gflags::ParseCommandLineFlags( &argc, &argv, true); CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; @@ -212,16 +61,17 @@ int main(int argc, char** argv) { const RoutingModel::NodeIndex kDepot(0); RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles); routing.SetDepot(kDepot); - // Setting first solution heuristic (cheapest addition). - FLAGS_routing_first_solution = "PathCheapestArc"; - // Disabling Large Neighborhood Search, comment out to activate it. - FLAGS_routing_no_lns = true; + RoutingSearchParameters parameters = + operations_research::BuildSearchParametersFromFlags(); + parameters.set_first_solution_strategy( + operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); + parameters.mutable_local_search_operators()->set_use_path_lns(false); // Setting up locations. const int64 kXMax = 100000; const int64 kYMax = 100000; const int64 kSpeed = 10; - LocationContainer locations(kSpeed); + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); for (int location = 0; location <= FLAGS_vrp_orders; ++location) { locations.AddRandomLocation(kXMax, kYMax); } @@ -233,7 +83,8 @@ int main(int argc, char** argv) { // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot); + RandomDemand demand(routing.nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), kNullCapacitySlack, kVehicleCapacity, @@ -250,7 +101,7 @@ int main(int argc, char** argv) { kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); // Adding time windows. - ACMRandom randomizer(GetSeed()); + ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; for (int order = 1; order < routing.nodes(); ++order) { const int64 start = randomizer.Uniform(kHorizon - kTWDuration); @@ -259,15 +110,15 @@ int main(int argc, char** argv) { // Adding resource constraints at the depot (start and end location of // routes). - std::vector start_end_times; + std::vector start_end_times; for (int i = 0; i < FLAGS_vrp_vehicles; ++i) { start_end_times.push_back(routing.CumulVar(routing.End(i), kTime)); start_end_times.push_back(routing.CumulVar(routing.Start(i), kTime)); } // Build corresponding time intervals. const int64 kVehicleSetup = 180; - Solver* const solver = routing.solver(); - std::vector intervals; + operations_research::Solver* const solver = routing.solver(); + std::vector intervals; solver->MakeFixedDurationIntervalVarArray(start_end_times, kVehicleSetup, "depot_interval", &intervals); // Constrain the number of maximum simultaneous intervals at depot. @@ -290,9 +141,13 @@ int main(int argc, char** argv) { } // Solve, returns a solution if any (owned by RoutingModel). - const Assignment* solution = routing.Solve(); + const operations_research::Assignment* solution = + routing.SolveWithParameters(parameters); if (solution != NULL) { - DisplayPlan(routing, *solution); + DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false, + /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); } else { LOG(INFO) << "No solution found."; } diff --git a/examples/cpp/cvrptw_with_stop_times_and_resources.cc b/examples/cpp/cvrptw_with_stop_times_and_resources.cc index b0b6fa8c27..a08fc73069 100644 --- a/examples/cpp/cvrptw_with_stop_times_and_resources.cc +++ b/examples/cpp/cvrptw_with_stop_times_and_resources.cc @@ -19,7 +19,6 @@ // limits the number of vehicles which can simultaneously leave or enter a node // to one. -#include #include #include "base/callback.h" @@ -27,25 +26,25 @@ #include "base/commandlineflags.h" #include "base/integral_types.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "base/join.h" #include "constraint_solver/routing.h" +#include "constraint_solver/routing_flags.h" +#include "cpp/cvrptw_lib.h" #include "base/random.h" -using operations_research::Assignment; using operations_research::IntervalVar; -using operations_research::IntExpr; -using operations_research::IntVar; -using operations_research::RoutingDimension; using operations_research::RoutingModel; -using operations_research::Solver; +using operations_research::RoutingSearchParameters; +using operations_research::LocationContainer; +using operations_research::RandomDemand; +using operations_research::ServiceTimePlusTransition; +using operations_research::GetSeed; +using operations_research::StopServiceTimePlusTransition; using operations_research::ACMRandom; using operations_research::StrCat; using operations_research::StringAppendF; using operations_research::StringPrintf; -DECLARE_string(routing_first_solution); -DECLARE_bool(routing_no_lns); DEFINE_int32(vrp_stops, 25, "Stop locations in the problem."); DEFINE_int32(vrp_orders_per_stop, 5, "Nodes for each stop."); DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance."); @@ -55,180 +54,6 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false, const char* kTime = "Time"; const char* kCapacity = "Capacity"; -// Random seed generator. -int32 GetSeed() { - if (FLAGS_vrp_use_deterministic_random_seed) { - return ACMRandom::DeterministicSeed(); - } else { - return ACMRandom::HostnamePidTimeSeed(); - } -} - -// Location container, contains positions of orders and can be used to obtain -// Manhattan distances/times between locations. -class LocationContainer { - public: - explicit LocationContainer(int64 speed) - : randomizer_(GetSeed()), speed_(speed) { - CHECK_LT(0, speed_); - } - void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); } - void AddRandomLocation(int64 x_max, int64 y_max, int duplicates) { - const int64 x = randomizer_.Uniform(x_max + 1); - const int64 y = randomizer_.Uniform(y_max + 1); - for (int i = 0; i < duplicates; ++i) { - AddLocation(x, y); - } - } - int64 ManhattanDistance(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return locations_[from].DistanceTo(locations_[to]); - } - int64 ManhattanTime(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return ManhattanDistance(from, to) / speed_; - } - bool SameLocation(RoutingModel::NodeIndex node1, - RoutingModel::NodeIndex node2) const { - if (node1 < locations_.size() && node2 < locations_.size()) { - return locations_[node1].IsAtSameLocation(locations_[node2]); - } - return false; - } - int64 SameLocationFromIndex(int64 node1, int64 node2) const { - // The direct conversion from constraint model indices to routing model - // nodes is correct because the depot is node 0. - // TODO(user): Fetch proper indices from routing model. - return SameLocation(RoutingModel::NodeIndex(node1), - RoutingModel::NodeIndex(node2)); - } - - private: - class Location { - public: - Location() : x_(0), y_(0) {} - Location(int64 x, int64 y) : x_(x), y_(y) {} - int64 DistanceTo(const Location& location) const { - return Abs(x_ - location.x_) + Abs(y_ - location.y_); - } - bool IsAtSameLocation(const Location& location) const { - return x_ == location.x_ && y_ == location.y_; - } - - private: - static int64 Abs(int64 value) { return std::max(value, -value); } - - int64 x_; - int64 y_; - }; - - ACMRandom randomizer_; - const int64 speed_; - ITIVector locations_; -}; - -// Random demand. -class RandomDemand { - public: - RandomDemand(int size, RoutingModel::NodeIndex depot) - : size_(size), depot_(depot) { - CHECK_LT(0, size_); - } - void Initialize() { - const int64 kDemandMax = 5; - const int64 kDemandMin = 1; - demand_.reset(new int64[size_]); - ACMRandom randomizer(GetSeed()); - for (int order = 0; order < size_; ++order) { - if (order == depot_) { - demand_[order] = 0; - } else { - demand_[order] = - kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1); - } - } - } - int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const { - return demand_[from.value()]; - } - - private: - std::unique_ptr demand_; - const int size_; - const RoutingModel::NodeIndex depot_; -}; - -// Stop service time + transition time callback. -class StopServiceTimePlusTransition { - public: - StopServiceTimePlusTransition(int64 stop_time, - const LocationContainer& location_container, - RoutingModel::NodeEvaluator2* transition_time) - : stop_time_(stop_time), - location_container_(location_container), - transition_time_(transition_time) {} - int64 Compute(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { - return location_container_.SameLocation(from, to) - ? 0 - : stop_time_ + transition_time_->Run(from, to); - } - - private: - const int64 stop_time_; - const LocationContainer& location_container_; - std::unique_ptr demand_; - std::unique_ptr transition_time_; -}; - -// Route plan displayer. -// TODO(user): Move the display code to the routing library. -void DisplayPlan(const RoutingModel& routing, const Assignment& plan) { - // Display plan cost. - std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue()); - - // Display dropped orders. - std::string dropped; - for (int order = 1; order < routing.nodes(); ++order) { - if (plan.Value(routing.NextVar(order)) == order) { - if (dropped.empty()) { - StringAppendF(&dropped, " %d", order); - } else { - StringAppendF(&dropped, ", %d", order); - } - } - } - if (!dropped.empty()) { - plan_output += "Dropped orders:" + dropped + "\n"; - } - LOG(INFO) << plan_output; - - // Display actual output for each vehicle. - const RoutingDimension& capacity_dimension = - routing.GetDimensionOrDie(kCapacity); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); - for (int route_number = 0; route_number < routing.vehicles(); - ++route_number) { - std::string route_output; - int64 order = routing.Start(route_number); - StringAppendF(&route_output, "Route %d: ", route_number); - if (routing.IsEnd(plan.Value(routing.NextVar(order)))) { - route_output += "Empty\n"; - } else { - while (true) { - IntVar* const load_var = capacity_dimension.CumulVar(order); - IntVar* const time_var = time_dimension.CumulVar(order); - StringAppendF(&route_output, " -> %lld Load(%lld) Time(%lld, %lld)", - order, plan.Value(load_var), plan.Min(time_var), - plan.Max(time_var)); - if (routing.IsEnd(order)) break; - order = plan.Value(routing.NextVar(order)); - } - } - LOG(INFO) << route_output; - } -} - int main(int argc, char** argv) { gflags::ParseCommandLineFlags( &argc, &argv, true); CHECK_LT(0, FLAGS_vrp_stops) << "Specify an instance size greater than 0."; @@ -241,16 +66,17 @@ int main(int argc, char** argv) { const RoutingModel::NodeIndex kDepot(0); RoutingModel routing(vrp_orders + 1, FLAGS_vrp_vehicles); routing.SetDepot(kDepot); - // Setting first solution heuristic (cheapest addition). - FLAGS_routing_first_solution = "PathCheapestArc"; - // Disabling Large Neighborhood Search, comment out to activate it. - FLAGS_routing_no_lns = true; + RoutingSearchParameters parameters = + operations_research::BuildSearchParametersFromFlags(); + parameters.set_first_solution_strategy( + operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); + parameters.mutable_local_search_operators()->set_use_path_lns(false); // Setting up locations. const int64 kXMax = 100000; const int64 kYMax = 100000; const int64 kSpeed = 10; - LocationContainer locations(kSpeed); + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); for (int stop = 0; stop <= FLAGS_vrp_stops; ++stop) { const int num_orders = stop == 0 ? 1 : FLAGS_vrp_orders_per_stop; locations.AddRandomLocation(kXMax, kYMax, num_orders); @@ -263,7 +89,8 @@ int main(int argc, char** argv) { // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot); + RandomDemand demand(routing.nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), kNullCapacitySlack, kVehicleCapacity, @@ -280,7 +107,7 @@ int main(int argc, char** argv) { kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); // Adding time windows, for the sake of simplicty same for each stop. - ACMRandom randomizer(GetSeed()); + ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) { const int64 start = randomizer.Uniform(kHorizon - kTWDuration); @@ -292,7 +119,7 @@ int main(int argc, char** argv) { } // Adding resource constraints at order locations. - Solver* const solver = routing.solver(); + operations_research::Solver* const solver = routing.solver(); std::vector intervals; for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) { std::vector stop_intervals; @@ -304,14 +131,15 @@ int main(int argc, char** argv) { intervals.push_back(interval); stop_intervals.push_back(interval); // Link order and interval. - IntVar* const order_start = routing.CumulVar(order, kTime); + operations_research::IntVar* const order_start = + routing.CumulVar(order, kTime); solver->AddConstraint( solver->MakeIsEqualCt(interval->SafeStartExpr(0), order_start, interval->PerformedExpr()->Var())); // Make interval performed iff corresponding order has service time. // An order has no service time iff it is at the same location as the // next order on the route. - IntVar* const is_null_duration = + operations_research::IntVar* const is_null_duration = solver->MakeElement([&locations, order](int64 index) { return locations.SameLocationFromIndex(order, index); }, routing.NextVar(order))->Var(); @@ -343,9 +171,13 @@ int main(int argc, char** argv) { } // Solve, returns a solution if any (owned by RoutingModel). - const Assignment* solution = routing.Solve(); + const operations_research::Assignment* solution = + routing.SolveWithParameters(parameters); if (solution != NULL) { - DisplayPlan(routing, *solution); + DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false, + /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); LOG(INFO) << "Stop intervals:"; for (IntervalVar* const interval : intervals) { if (solution->PerformedValue(interval)) { diff --git a/examples/cpp/model_util.cc b/examples/cpp/model_util.cc index 143acca803..8e572f3b2e 100644 --- a/examples/cpp/model_util.cc +++ b/examples/cpp/model_util.cc @@ -73,10 +73,10 @@ std::string ConstraintLabel(int index) { return StringPrintf("ct_%i", index); } // Scans argument to add links in the graph. template -void ExportLinks(const CPModelProto& model, const std::string& source, - const T& proto, GraphExporter* const exporter) { +void ExportLinks(const CpModel& model, const std::string& source, const T& proto, + GraphExporter* const exporter) { const std::string& arg_name = model.tags(proto.argument_index()); - if (proto.has_integer_expression_index()) { + if (proto.type() == CpArgument::EXPRESSION) { exporter->WriteLink(source, ExprLabel(proto.integer_expression_index()), arg_name); } @@ -84,7 +84,7 @@ void ExportLinks(const CPModelProto& model, const std::string& source, exporter->WriteLink(source, ExprLabel(proto.integer_expression_array(i)), arg_name); } - if (proto.has_interval_index()) { + if (proto.type() == CpArgument::INTERVAL) { exporter->WriteLink(source, IntervalLabel(proto.interval_index()), arg_name); } @@ -92,7 +92,7 @@ void ExportLinks(const CPModelProto& model, const std::string& source, exporter->WriteLink(source, IntervalLabel(proto.interval_array(i)), arg_name); } - if (proto.has_sequence_index()) { + if (proto.type() == CpArgument::SEQUENCE) { exporter->WriteLink(source, SequenceLabel(proto.sequence_index()), arg_name); } @@ -104,8 +104,7 @@ void ExportLinks(const CPModelProto& model, const std::string& source, // Scans the expression protobuf to see if it corresponds to an // integer variable with min_value == max_value. -bool GetValueIfConstant(const CPModelProto& model, - const CPIntegerExpressionProto& proto, +bool GetValueIfConstant(const CpModel& model, const CpIntegerExpression& proto, int64* const value) { CHECK_NOTNULL(value); const int expr_type = proto.type_index(); @@ -115,12 +114,12 @@ bool GetValueIfConstant(const CPModelProto& model, if (proto.arguments_size() != 2) { return false; } - const CPArgumentProto& arg1_proto = proto.arguments(0); + const CpArgument& arg1_proto = proto.arguments(0); if (model.tags(arg1_proto.argument_index()) != ModelVisitor::kMinArgument) { return false; } const int64 value1 = arg1_proto.integer_value(); - const CPArgumentProto& arg2_proto = proto.arguments(1); + const CpArgument& arg2_proto = proto.arguments(1); if (model.tags(arg2_proto.argument_index()) != ModelVisitor::kMaxArgument) { return false; } @@ -134,12 +133,12 @@ bool GetValueIfConstant(const CPModelProto& model, } // Declares a labelled expression in the graph file. -void DeclareExpression(int index, const CPModelProto& proto, +void DeclareExpression(int index, const CpModel& proto, GraphExporter* const exporter) { - const CPIntegerExpressionProto& expr = proto.expressions(index); + const CpIntegerExpression& expr = proto.expressions(index); const std::string label = ExprLabel(index); int64 value = 0; - if (expr.has_name()) { + if (!expr.name().empty()) { exporter->WriteNode(label, expr.name(), "oval", kGreen1); } else if (GetValueIfConstant(proto, expr, &value)) { exporter->WriteNode(label, StringPrintf("%lld", value), "oval", kYellow); @@ -149,11 +148,11 @@ void DeclareExpression(int index, const CPModelProto& proto, } } -void DeclareInterval(int index, const CPModelProto& proto, +void DeclareInterval(int index, const CpModel& proto, GraphExporter* const exporter) { - const CPIntervalVariableProto& interval = proto.intervals(index); + const CpIntervalVariable& interval = proto.intervals(index); const std::string label = IntervalLabel(index); - if (interval.has_name()) { + if (!interval.name().empty()) { exporter->WriteNode(label, interval.name(), "circle", kGreen2); } else { const std::string& type = proto.tags(interval.type_index()); @@ -161,11 +160,11 @@ void DeclareInterval(int index, const CPModelProto& proto, } } -void DeclareSequence(int index, const CPModelProto& proto, +void DeclareSequence(int index, const CpModel& proto, GraphExporter* const exporter) { - const CPSequenceVariableProto& sequence = proto.sequences(index); + const CpSequenceVariable& sequence = proto.sequences(index); const std::string label = SequenceLabel(index); - if (sequence.has_name()) { + if (!sequence.name().empty()) { exporter->WriteNode(label, sequence.name(), "circle", kGreen3); } else { const std::string& type = proto.tags(sequence.type_index()); @@ -173,16 +172,16 @@ void DeclareSequence(int index, const CPModelProto& proto, } } -void DeclareConstraint(int index, const CPModelProto& proto, +void DeclareConstraint(int index, const CpModel& proto, GraphExporter* const exporter) { - const CPConstraintProto& ct = proto.constraints(index); + const CpConstraint& ct = proto.constraints(index); const std::string& type = proto.tags(ct.type_index()); const std::string label = ConstraintLabel(index); exporter->WriteNode(label, type, "rectangle", kBlue); } // Parses the proto and exports it to a graph file. -void ExportToGraphFile(const CPModelProto& proto, File* const file, +void ExportToGraphFile(const CpModel& proto, File* const file, GraphExporter::GraphFormat format) { std::unique_ptr exporter( GraphExporter::MakeFileExporter(file, format)); @@ -210,7 +209,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file, } for (int i = 0; i < proto.expressions_size(); ++i) { - const CPIntegerExpressionProto& expr = proto.expressions(i); + const CpIntegerExpression& expr = proto.expressions(i); const std::string label = ExprLabel(i); for (int j = 0; j < expr.arguments_size(); ++j) { ExportLinks(proto, label, expr.arguments(j), exporter.get()); @@ -218,7 +217,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file, } for (int i = 0; i < proto.intervals_size(); ++i) { - const CPIntervalVariableProto& interval = proto.intervals(i); + const CpIntervalVariable& interval = proto.intervals(i); const std::string label = IntervalLabel(i); for (int j = 0; j < interval.arguments_size(); ++j) { ExportLinks(proto, label, interval.arguments(j), exporter.get()); @@ -226,7 +225,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file, } for (int i = 0; i < proto.sequences_size(); ++i) { - const CPSequenceVariableProto& sequence = proto.sequences(i); + const CpSequenceVariable& sequence = proto.sequences(i); const std::string label = SequenceLabel(i); for (int j = 0; j < sequence.arguments_size(); ++j) { ExportLinks(proto, label, sequence.arguments(j), exporter.get()); @@ -234,7 +233,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file, } for (int i = 0; i < proto.constraints_size(); ++i) { - const CPConstraintProto& ct = proto.constraints(i); + const CpConstraint& ct = proto.constraints(i); const std::string label = ConstraintLabel(i); for (int j = 0; j < ct.arguments_size(); ++j) { ExportLinks(proto, label, ct.arguments(j), exporter.get()); @@ -242,7 +241,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file, } if (proto.has_objective()) { - const CPObjectiveProto& obj = proto.objective(); + const CpObjective& obj = proto.objective(); exporter->WriteLink(kObjLabel, ExprLabel(obj.objective_index()), ModelVisitor::kExpressionArgument); } @@ -254,13 +253,13 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file, int Run() { // ----- Load input file into protobuf ----- - File* const file = File::Open(FLAGS_input, "r"); - if (file == NULL) { + File* file; + if (!file::Open(FLAGS_input, "r", &file, file::Defaults()).ok()) { LOG(WARNING) << "Cannot open " << FLAGS_input; return kProblem; } - CPModelProto model_proto; + CpModel model_proto; RecordReader reader(file); if (!(reader.ReadProtocolMessage(&model_proto) && reader.Close())) { LOG(INFO) << "No model found in " << file->filename(); @@ -270,7 +269,7 @@ int Run() { // ----- Display loaded protobuf ----- LOG(INFO) << "Read model " << model_proto.model(); - if (model_proto.has_license_text()) { + if (!model_proto.license_text().empty()) { LOG(INFO) << "License = " << model_proto.license_text(); } @@ -296,8 +295,9 @@ int Run() { } if (!FLAGS_insert_license.empty()) { - File* const license = File::Open(FLAGS_insert_license, "rb"); - if (license == NULL) { + File* license; + if (!file::Open(FLAGS_insert_license, "rb", &license, file::Defaults()) + .ok()) { LOG(WARNING) << "Cannot open " << FLAGS_insert_license; return kProblem; } @@ -306,7 +306,7 @@ int Run() { license->Read(text, size); text[size] = '\0'; model_proto.set_license_text(text); - license->Close(); + license->Close(file::Defaults()).IgnoreError(); } // ----- Reporting ----- @@ -355,8 +355,8 @@ int Run() { // ----- Output ----- if (!FLAGS_output.empty()) { - File* const output = File::Open(FLAGS_output, "wb"); - if (output == NULL) { + File* output; + if (!file::Open(FLAGS_output, "wb", &output, file::Defaults()).ok()) { LOG(INFO) << "Cannot open " << FLAGS_output; return kProblem; } @@ -369,23 +369,23 @@ int Run() { } if (!FLAGS_dot_file.empty()) { - File* const dot_file = File::Open(FLAGS_dot_file, "w"); - if (dot_file == NULL) { + File* dot_file; + if (!file::Open(FLAGS_dot_file, "w", &dot_file, file::Defaults()).ok()) { LOG(INFO) << "Cannot open " << FLAGS_dot_file; return kProblem; } ExportToGraphFile(model_proto, dot_file, GraphExporter::DOT_FORMAT); - dot_file->Close(); + dot_file->Close(file::Defaults()).IgnoreError(); } if (!FLAGS_gml_file.empty()) { - File* const gml_file = File::Open(FLAGS_gml_file, "w"); - if (gml_file == NULL) { + File* gml_file; + if (!file::Open(FLAGS_gml_file, "w", &gml_file, file::Defaults()).ok()) { LOG(INFO) << "Cannot open " << FLAGS_gml_file; return kProblem; } ExportToGraphFile(model_proto, gml_file, GraphExporter::GML_FORMAT); - gml_file->Close(); + gml_file->Close(file::Defaults()).IgnoreError(); } return kOk; } diff --git a/examples/cpp/nqueens.cc b/examples/cpp/nqueens.cc index 057655763c..7b6ebe9d4f 100644 --- a/examples/cpp/nqueens.cc +++ b/examples/cpp/nqueens.cc @@ -36,7 +36,7 @@ DEFINE_int32( size, 0, "Size of the problem. If equal to 0, will test several increasing sizes."); DEFINE_bool(use_symmetry, false, "Use Symmetry Breaking methods"); -DECLARE_bool(cp_no_solve); +DECLARE_bool(cp_disable_solve); static const int kNumSolutions[] = { 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712, 365596, 2279184}; @@ -174,13 +174,13 @@ void CheckNumberOfSolutions(int size, int num_solutions) { if (FLAGS_use_symmetry) { if (size - 1 < kKnownUniqueSolutions) { CHECK_EQ(num_solutions, kNumUniqueSolutions[size - 1]); - } else if (!FLAGS_cp_no_solve) { + } else if (!FLAGS_cp_disable_solve) { CHECK_GT(num_solutions, 0); } } else { if (size - 1 < kKnownSolutions) { CHECK_EQ(num_solutions, kNumSolutions[size - 1]); - } else if (!FLAGS_cp_no_solve) { + } else if (!FLAGS_cp_disable_solve) { CHECK_GT(num_solutions, 0); } } diff --git a/examples/cpp/pdptw.cc b/examples/cpp/pdptw.cc index eca56e2989..d9b2945443 100644 --- a/examples/cpp/pdptw.cc +++ b/examples/cpp/pdptw.cc @@ -46,14 +46,13 @@ #include "base/split.h" #include "base/mathutil.h" #include "constraint_solver/routing.h" +#include "constraint_solver/routing_flags.h" -DECLARE_bool(routing_no_lns); DEFINE_string(pdp_file, "", "File containing the Pickup and Delivery Problem to solve."); DEFINE_int32(pdp_force_vehicles, 0, "Force the number of vehicles used (maximum number of routes."); -DEFINE_bool(pdp_display_solution, false, - "Displays the solution of the Pickup and Delivery Problem."); +DECLARE_string(routing_first_solution); namespace operations_research { @@ -279,11 +278,15 @@ bool LoadAndSolve(const std::string& pdp_file) { } // Set up search parameters. - routing.set_first_solution_strategy(RoutingModel::ROUTING_ALL_UNPERFORMED); - FLAGS_routing_no_lns = true; + RoutingSearchParameters parameters = BuildSearchParametersFromFlags(); + if (FLAGS_routing_first_solution.empty()) { + parameters.set_first_solution_strategy( + operations_research::FirstSolutionStrategy::ALL_UNPERFORMED); + } + parameters.mutable_local_search_operators()->set_use_path_lns(false); // Solve pickup and delivery problem. - const Assignment* assignment = routing.Solve(NULL); + const Assignment* assignment = routing.SolveWithParameters(parameters); if (NULL != assignment) { LOG(INFO) << "Cost: " << assignment->ObjectiveValue(); LOG(INFO) << VerboseOutput(routing, *assignment, coords, service_times); diff --git a/examples/csharp/cscvrptw.cs b/examples/csharp/cscvrptw.cs index a583d86444..ce3856f6d4 100644 --- a/examples/csharp/cscvrptw.cs +++ b/examples/csharp/cscvrptw.cs @@ -259,13 +259,13 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { } // Solving - RoutingSearchParameters parameters = new RoutingSearchParameters(); - parameters.no_lns = true; - parameters.first_solution = "AllUnperformed"; - parameters.trace = true; + RoutingSearchParameters search_parameters = + RoutingModel.DefaultSearchParameters(); + search_parameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.ALL_UNPERFORMED; Console.WriteLine("Search"); - Assignment solution = model.SolveWithParameters(parameters, null); + Assignment solution = model.SolveWithParameters(search_parameters); if (solution != null) { String output = "Total cost: " + solution.ObjectiveValue() + "\n"; diff --git a/examples/csharp/cstsp.cs b/examples/csharp/cstsp.cs index 1e8e48b083..c9e3550c9d 100644 --- a/examples/csharp/cstsp.cs +++ b/examples/csharp/cstsp.cs @@ -48,8 +48,6 @@ class Tsp static void Solve(int size, int forbidden, int seed) { RoutingModel routing = new RoutingModel(size, 1); - // Setting first solution heuristic (cheapest addition). - routing.SetFirstSolutionStrategy(RoutingModel.ROUTING_PATH_CHEAPEST_ARC); // Setting the cost function. // Put a permanent callback to the distance accessor here. The callback @@ -79,6 +77,12 @@ class Tsp "dummy"); // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters search_parameters = + RoutingModel.DefaultSearchParameters(); + // Setting first solution heuristic (cheapest addition). + search_parameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PATH_CHEAPEST_ARC; + Assignment solution = routing.Solve(); if (solution != null) { // Solution cost. diff --git a/examples/python/rabbit_pheasant.py b/examples/python/rabbit_pheasant.py index c605a79425..d25ddf7e82 100644 --- a/examples/python/rabbit_pheasant.py +++ b/examples/python/rabbit_pheasant.py @@ -21,11 +21,16 @@ flavors of constraint programming interfaces. """ from __future__ import print_function from ortools.constraint_solver import pywrapcp +from ortools.constraint_solver import solver_parameters_pb2 def main(): + parameters = pywrapcp.Solver.DefaultSolverParameters() + parameters.trace_search = True + # Create the solver. - solver = pywrapcp.Solver('rabbit+pheasant') + solver = pywrapcp.Solver('rabbit+pheasant', parameters) + # Create the variables. pheasant = solver.IntVar(0, 100, 'pheasant') diff --git a/examples/python/tsp.py b/examples/python/tsp.py index eeb40091c6..48db456986 100644 --- a/examples/python/tsp.py +++ b/examples/python/tsp.py @@ -28,6 +28,8 @@ import random import argparse from ortools.constraint_solver import pywrapcp +# You need to import routing_enums_pb2 after pywrapcp! +from ortools.constraint_solver import routing_enums_pb2 parser = argparse.ArgumentParser() parser.add_argument('--tsp_size', default = 10, type = int, @@ -75,23 +77,16 @@ class RandomMatrix(object): def main(args): # Create routing model if args.tsp_size > 0: - # Set a global parameter. - param = pywrapcp.RoutingParameters() - param.use_light_propagation = args.light_propagation - pywrapcp.RoutingModel.SetGlobalParameters(param) - # TSP of size args.tsp_size # Second argument = 1 to build a single tour (it's a TSP). # Nodes are indexed from 0 to parser_tsp_size - 1, by default the start of # the route is node 0. routing = pywrapcp.RoutingModel(args.tsp_size, 1) - parameters = pywrapcp.RoutingSearchParameters() + search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() # Setting first solution heuristic (cheapest addition). - parameters.first_solution = 'PathCheapestArc' - # Disabling Large Neighborhood Search, comment out to activate it. - parameters.no_lns = True - parameters.no_tsp = False + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # Setting the cost function. # Put a callback to the distance accessor here. The callback takes two @@ -116,7 +111,8 @@ def main(args): forbidden_connections += 1 # Solve, returns a solution if any. - assignment = routing.SolveWithParameters(parameters, None) +# assignment = routing.SolveWithParameters(search_parameters) + assignment = routing.Solve() if assignment: # Solution cost. print(assignment.ObjectiveValue()) diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index 29dc385783..0d16953957 100644 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -460,6 +460,7 @@ CONSTRAINT_SOLVER_LIB_OBJS = \ $(OBJ_DIR)/constraint_solver/sched_search.$O\ $(OBJ_DIR)/constraint_solver/search.$O\ $(OBJ_DIR)/constraint_solver/search_limit.pb.$O\ + $(OBJ_DIR)/constraint_solver/solver_parameters.pb.$O\ $(OBJ_DIR)/constraint_solver/table.$O\ $(OBJ_DIR)/constraint_solver/timetabling.$O\ $(OBJ_DIR)/constraint_solver/trace.$O\ @@ -470,7 +471,7 @@ CONSTRAINT_SOLVER_LIB_OBJS = \ $(OBJ_DIR)/constraint_solver/alldiff_cst.$O:$(SRC_DIR)/constraint_solver/alldiff_cst.cc $(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/alldiff_cst.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Salldiff_cst.$O -$(OBJ_DIR)/constraint_solver/assignment.$O:$(SRC_DIR)/constraint_solver/assignment.cc $(GEN_DIR)/constraint_solver/assignment.pb.h +$(OBJ_DIR)/constraint_solver/assignment.$O:$(SRC_DIR)/constraint_solver/assignment.cc $(GEN_DIR)/constraint_solver/assignment.pb.h $(SRC_DIR)/constraint_solver/constraint_solver.h $(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/assignment.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Sassignment.$O $(OBJ_DIR)/constraint_solver/assignment.pb.$O:$(GEN_DIR)/constraint_solver/assignment.pb.cc @@ -490,6 +491,16 @@ $(GEN_DIR)/constraint_solver/assignment.pb.h:$(GEN_DIR)/constraint_solver/assign $(OBJ_DIR)/constraint_solver/collect_variables.$O:$(SRC_DIR)/constraint_solver/collect_variables.cc $(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/collect_variables.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Scollect_variables.$O +$(GEN_DIR)/constraint_solver/solver_parameters.pb.cc:$(SRC_DIR)/constraint_solver/solver_parameters.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(INC_DIR) --cpp_out=$(GEN_DIR) $(SRC_DIR)/constraint_solver/solver_parameters.proto + +$(GEN_DIR)/constraint_solver/solver_parameters.pb.h:$(GEN_DIR)/constraint_solver/solver_parameters.pb.cc + +$(OBJ_DIR)/constraint_solver/solver_parameters.pb.$O:$(GEN_DIR)/constraint_solver/solver_parameters.pb.cc + $(CCC) $(CFLAGS) -c $(GEN_DIR)/constraint_solver/solver_parameters.pb.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Ssolver_parameters.pb.$O + +$(SRC_DIR)/constraint_solver/constraint_solver.h: $(GEN_DIR)/constraint_solver/solver_parameters.pb.h + $(OBJ_DIR)/constraint_solver/constraint_solver.$O:$(SRC_DIR)/constraint_solver/constraint_solver.cc $(GEN_DIR)/constraint_solver/model.pb.h $(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/constraint_solver.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Sconstraint_solver.$O @@ -703,6 +714,7 @@ UTIL_LIB_OBJS=\ $(OBJ_DIR)/util/graph_export.$O \ $(OBJ_DIR)/util/piecewise_linear_function.$O \ $(OBJ_DIR)/util/proto_tools.$O \ + $(OBJ_DIR)/util/range_query_function.$O \ $(OBJ_DIR)/util/rational_approximation.$O \ $(OBJ_DIR)/util/stats.$O \ $(OBJ_DIR)/util/time_limit.$O \ @@ -726,6 +738,9 @@ $(OBJ_DIR)/util/piecewise_linear_function.$O:$(SRC_DIR)/util/piecewise_linear_fu $(OBJ_DIR)/util/proto_tools.$O:$(SRC_DIR)/util/proto_tools.cc $(GEN_DIR)/linear_solver/linear_solver.pb.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sutil$Sproto_tools.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Sproto_tools.$O +$(OBJ_DIR)/util/range_query_function.$O:$(SRC_DIR)/util/range_query_function.cc $(GEN_DIR)/linear_solver/linear_solver.pb.h + $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sutil$Srange_query_function.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Srange_query_function.$O + $(OBJ_DIR)/util/rational_approximation.$O:$(SRC_DIR)/util/rational_approximation.cc $(CCC) $(CFLAGS) -c $(SRC_DIR)/util/rational_approximation.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Srational_approximation.$O @@ -817,12 +832,36 @@ endif ROUTING_LIB_OBJS=\ $(OBJ_DIR)/constraint_solver/routing.$O \ + $(OBJ_DIR)/constraint_solver/routing_enums.pb.$O \ + $(OBJ_DIR)/constraint_solver/routing_flags.$O \ + $(OBJ_DIR)/constraint_solver/routing_parameters.pb.$O \ $(OBJ_DIR)/constraint_solver/routing_search.$O -$(OBJ_DIR)/constraint_solver/routing.$O:$(SRC_DIR)/constraint_solver/routing.cc +$(GEN_DIR)/constraint_solver/routing_enums.pb.cc:$(SRC_DIR)/constraint_solver/routing_enums.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(INC_DIR) --cpp_out=$(GEN_DIR) $(SRC_DIR)/constraint_solver/routing_enums.proto + +$(GEN_DIR)/constraint_solver/routing_enums.pb.h:$(GEN_DIR)/constraint_solver/routing_enums.pb.cc + +$(OBJ_DIR)/constraint_solver/routing_enums.pb.$O:$(GEN_DIR)/constraint_solver/routing_enums.pb.cc + $(CCC) $(CFLAGS) -c $(GEN_DIR)/constraint_solver/routing_enums.pb.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Srouting_enums.pb.$O + +$(GEN_DIR)/constraint_solver/routing_parameters.pb.cc:$(SRC_DIR)/constraint_solver/routing_parameters.proto $(GEN_DIR)/constraint_solver/routing_enums.pb.h + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(INC_DIR) --cpp_out=$(GEN_DIR) $(SRC_DIR)/constraint_solver/routing_parameters.proto + +$(GEN_DIR)/constraint_solver/routing_parameters.pb.h:$(GEN_DIR)/constraint_solver/routing_parameters.pb.cc + +$(OBJ_DIR)/constraint_solver/routing_parameters.pb.$O:$(GEN_DIR)/constraint_solver/routing_parameters.pb.cc + $(CCC) $(CFLAGS) -c $(GEN_DIR)/constraint_solver/routing_parameters.pb.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Srouting_parameters.pb.$O + +$(SRC_DIR)/constraint_solver/routing.h: $(GEN_DIR)/constraint_solver/routing_parameters.pb.h + +$(OBJ_DIR)/constraint_solver/routing.$O:$(SRC_DIR)/constraint_solver/routing.cc $(SRC_DIR)/constraint_solver/routing.h $(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/routing.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Srouting.$O -$(OBJ_DIR)/constraint_solver/routing_search.$O:$(SRC_DIR)/constraint_solver/routing_search.cc +$(OBJ_DIR)/constraint_solver/routing_flags.$O:$(SRC_DIR)/constraint_solver/routing_flags.cc $(SRC_DIR)/constraint_solver/routing.h + $(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/routing_flags.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Srouting_flags.$O + +$(OBJ_DIR)/constraint_solver/routing_search.$O:$(SRC_DIR)/constraint_solver/routing_search.cc $(SRC_DIR)/constraint_solver/routing.h $(CCC) $(CFLAGS) -c $(SRC_DIR)/constraint_solver/routing_search.cc $(OBJ_OUT)$(OBJ_DIR)$Sconstraint_solver$Srouting_search.$O $(LIB_DIR)/$(LIBPREFIX)routing.$(DYNAMIC_LIB_SUFFIX): $(ROUTING_LIB_OBJS) @@ -1246,29 +1285,32 @@ $(OBJ_DIR)/cryptarithm.$O:$(EX_DIR)/cpp/cryptarithm.cc $(SRC_DIR)/constraint_sol $(BIN_DIR)/cryptarithm$E: $(DYNAMIC_CP_DEPS) $(OBJ_DIR)/cryptarithm.$O $(CCC) $(CFLAGS) $(OBJ_DIR)/cryptarithm.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scryptarithm$E -$(OBJ_DIR)/cvrptw.$O: $(EX_DIR)/cpp/cvrptw.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h +$(OBJ_DIR)/cvrptw_lib.$O: $(EX_DIR)/cpp/cvrptw_lib.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h + $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/cvrptw_lib.cc $(OBJ_OUT)$(OBJ_DIR)$Scvrptw_lib.$O + +$(OBJ_DIR)/cvrptw.$O: $(EX_DIR)/cpp/cvrptw.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h $(EX_DIR)/cpp/cvrptw_lib.h $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/cvrptw.cc $(OBJ_OUT)$(OBJ_DIR)$Scvrptw.$O -$(BIN_DIR)/cvrptw$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw.$O - $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw$E +$(BIN_DIR)/cvrptw$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw.$O $(OBJ_DIR)/cvrptw_lib.$O + $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw.$O $(OBJ_DIR)/cvrptw_lib.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw$E -$(OBJ_DIR)/cvrptw_with_refueling.$O: $(EX_DIR)/cpp/cvrptw_with_refueling.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h +$(OBJ_DIR)/cvrptw_with_refueling.$O: $(EX_DIR)/cpp/cvrptw_with_refueling.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h $(EX_DIR)/cpp/cvrptw_lib.h $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/cvrptw_with_refueling.cc $(OBJ_OUT)$(OBJ_DIR)$Scvrptw_with_refueling.$O -$(BIN_DIR)/cvrptw_with_refueling$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw_with_refueling.$O - $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw_with_refueling.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw_with_refueling$E +$(BIN_DIR)/cvrptw_with_refueling$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw_with_refueling.$O $(OBJ_DIR)/cvrptw_lib.$O + $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw_with_refueling.$O $(OBJ_DIR)/cvrptw_lib.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw_with_refueling$E -$(OBJ_DIR)/cvrptw_with_resources.$O: $(EX_DIR)/cpp/cvrptw_with_resources.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h +$(OBJ_DIR)/cvrptw_with_resources.$O: $(EX_DIR)/cpp/cvrptw_with_resources.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h $(EX_DIR)/cpp/cvrptw_lib.h $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/cvrptw_with_resources.cc $(OBJ_OUT)$(OBJ_DIR)$Scvrptw_with_resources.$O -$(BIN_DIR)/cvrptw_with_resources$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw_with_resources.$O - $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw_with_resources.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw_with_resources$E +$(BIN_DIR)/cvrptw_with_resources$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw_with_resources.$O $(OBJ_DIR)/cvrptw_lib.$O + $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw_with_resources.$O $(OBJ_DIR)/cvrptw_lib.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw_with_resources$E -$(OBJ_DIR)/cvrptw_with_stop_times_and_resources.$O: $(EX_DIR)/cpp/cvrptw_with_stop_times_and_resources.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h +$(OBJ_DIR)/cvrptw_with_stop_times_and_resources.$O: $(EX_DIR)/cpp/cvrptw_with_stop_times_and_resources.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(SRC_DIR)/constraint_solver/routing.h $(EX_DIR)/cpp/cvrptw_lib.h $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/cvrptw_with_stop_times_and_resources.cc $(OBJ_OUT)$(OBJ_DIR)$Scvrptw_with_stop_times_and_resources.$O -$(BIN_DIR)/cvrptw_with_stop_times_and_resources$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw_with_stop_times_and_resources.$O - $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw_with_stop_times_and_resources.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw_with_stop_times_and_resources$E +$(BIN_DIR)/cvrptw_with_stop_times_and_resources$E: $(DYNAMIC_ROUTING_DEPS) $(OBJ_DIR)/cvrptw_with_stop_times_and_resources.$O $(OBJ_DIR)/cvrptw_lib.$O $(OBJ_DIR)/cvrptw_lib.$O + $(CCC) $(CFLAGS) $(OBJ_DIR)/cvrptw_with_stop_times_and_resources.$O $(OBJ_DIR)/cvrptw_lib.$O $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) $(EXE_OUT)$(BIN_DIR)$Scvrptw_with_stop_times_and_resources$E $(OBJ_DIR)/dobble_ls.$O:$(EX_DIR)/cpp/dobble_ls.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/dobble_ls.cc $(OBJ_OUT)$(OBJ_DIR)$Sdobble_ls.$O diff --git a/makefiles/Makefile.csharp.mk b/makefiles/Makefile.csharp.mk index 89c3706648..4577f94fe8 100644 --- a/makefiles/Makefile.csharp.mk +++ b/makefiles/Makefile.csharp.mk @@ -143,7 +143,7 @@ $(GEN_DIR)/com/google/ortools/SvnVersion$(GIT_REVISION).txt: # csharportools -csharportools: $(BIN_DIR)/$(CLR_DLL_NAME).dll +csharportools: $(BIN_DIR)/$(CLR_DLL_NAME).dll dependencies/install/bin/Google.Protobuf.dll $(GEN_DIR)/linear_solver/linear_solver_csharp_wrap.cc: \ $(SRC_DIR)/linear_solver/csharp/linear_solver.swig \ @@ -195,6 +195,22 @@ $(GEN_DIR)/graph/graph_csharp_wrap.cc: \ $(OBJ_DIR)/swig/graph_csharp_wrap.$O: $(GEN_DIR)/graph/graph_csharp_wrap.cc $(CCC) $(CFLAGS) -c $(GEN_DIR)$Sgraph$Sgraph_csharp_wrap.cc $(OBJ_OUT)$(OBJ_DIR)$Sswig$Sgraph_csharp_wrap.$O +# Protobufs + +$(GEN_DIR)/com/google/ortools/constraintsolver/SearchLimit.g.cs: $(SRC_DIR)/constraint_solver/search_limit.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --csharp_out=$(GEN_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver --csharp_opt=file_extension=.g.cs $(SRC_DIR)$Sconstraint_solver$Ssearch_limit.proto + +$(GEN_DIR)/com/google/ortools/constraintsolver/SolverParameters.g.cs: $(SRC_DIR)/constraint_solver/solver_parameters.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --csharp_out=$(GEN_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver --csharp_opt=file_extension=.g.cs $(SRC_DIR)$Sconstraint_solver$Ssolver_parameters.proto + +$(GEN_DIR)/com/google/ortools/constraintsolver/RoutingParameters.g.cs: $(SRC_DIR)/constraint_solver/routing_parameters.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --csharp_out=$(GEN_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver --csharp_opt=file_extension=.g.cs $(SRC_DIR)$Sconstraint_solver$Srouting_parameters.proto + +$(GEN_DIR)/com/google/ortools/constraintsolver/RoutingEnums.g.cs: $(SRC_DIR)/constraint_solver/routing_enums.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --csharp_out=$(GEN_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver --csharp_opt=file_extension=.g.cs $(SRC_DIR)$Sconstraint_solver$Srouting_enums.proto + +# Main DLL + $(BIN_DIR)/$(CLR_DLL_NAME).dll: \ $(OBJ_DIR)/swig/linear_solver_csharp_wrap.$O \ $(OBJ_DIR)/swig/constraint_solver_csharp_wrap.$O \ @@ -213,23 +229,28 @@ $(BIN_DIR)/$(CLR_DLL_NAME).dll: \ $(SRC_DIR)/com/google/ortools/linearsolver/SolverHelper.cs \ $(SRC_DIR)/com/google/ortools/linearsolver/VariableHelper.cs \ $(SRC_DIR)/com/google/ortools/util/NestedArrayHelper.cs \ + $(SRC_DIR)/com/google/ortools/util/ProtoHelper.cs \ $(GEN_DIR)/com/google/ortools/CommonAssemblyAttributes.cs \ + $(GEN_DIR)/com/google/ortools/constraintsolver/SearchLimit.g.cs\ + $(GEN_DIR)/com/google/ortools/constraintsolver/SolverParameters.g.cs\ + $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingParameters.g.cs\ + $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingEnums.g.cs\ $(STATIC_ALL_DEPS) ifeq ($(SYSTEM),win) - $(CSC) /target:module /out:$(LIB_DIR)$S$(LIBPREFIX)$(CLR_DLL_NAME).netmodule /warn:0 /nologo /debug $(GEN_DIR)\\com\\google\\ortools\\linearsolver\\*.cs $(SRC_DIR)\\com\\google\\ortools\\linearsolver\\*.cs $(GEN_DIR)\\com\\google\\ortools\\constraintsolver\\*.cs $(SRC_DIR)\\com\\google\\ortools\\constraintsolver\\*.cs $(GEN_DIR)\\com\\google\\ortools\\algorithms\\*.cs $(SRC_DIR)\\com\\google\\ortools\\algorithms\\*.cs $(GEN_DIR)\\com\\google\\ortools\\graph\\*.cs $(SRC_DIR)\\com\\google\\ortools\\util\\*.cs $(GEN_DIR)\\com\\google\\ortools\\CommonAssemblyAttributes.cs + $(CSC) /target:module /out:$(LIB_DIR)$S$(LIBPREFIX)$(CLR_DLL_NAME).netmodule /lib:$(DEP_BIN_DIR) /r:Google.Protobuf.dll /warn:0 /nologo /debug $(GEN_DIR)\\com\\google\\ortools\\linearsolver\\*.cs $(SRC_DIR)\\com\\google\\ortools\\linearsolver\\*.cs $(GEN_DIR)\\com\\google\\ortools\\constraintsolver\\*.cs $(SRC_DIR)\\com\\google\\ortools\\constraintsolver\\*.cs $(GEN_DIR)\\com\\google\\ortools\\algorithms\\*.cs $(SRC_DIR)\\com\\google\\ortools\\algorithms\\*.cs $(GEN_DIR)\\com\\google\\ortools\\graph\\*.cs $(SRC_DIR)\\com\\google\\ortools\\util\\*.cs $(GEN_DIR)\\com\\google\\ortools\\CommonAssemblyAttributes.cs $(DYNAMIC_LD) $(SIGNING_FLAGS) $(LDOUT)$(BIN_DIR)$S$(CLR_DLL_NAME).dll $(LIB_DIR)$S$(LIBPREFIX)$(CLR_DLL_NAME).netmodule $(OBJ_DIR)$Sswig$Slinear_solver_csharp_wrap.$O $(OBJ_DIR)$Sswig$Sconstraint_solver_csharp_wrap.$O $(OBJ_DIR)$Sswig$Sknapsack_solver_csharp_wrap.$O $(OBJ_DIR)$Sswig$Sgraph_csharp_wrap.$O $(STATIC_ALL_LNK) $(STATIC_LD_FLAGS) else - $(CSC) /target:library /out:$(BIN_DIR)/$(CLR_DLL_NAME).dll /warn:0 /nologo /debug $(SRC_DIR)/com/google/ortools/util/*.cs $(GEN_DIR)/com/google/ortools/linearsolver/*.cs $(SRC_DIR)/com/google/ortools/linearsolver/*.cs $(GEN_DIR)/com/google/ortools/constraintsolver/*.cs $(SRC_DIR)/com/google/ortools/constraintsolver/*.cs $(SRC_DIR)/com/google/ortools/algorithms/*.cs $(GEN_DIR)/com/google/ortools/algorithms/*.cs $(GEN_DIR)/com/google/ortools/graph/*.cs $(GEN_DIR)/com/google/ortools/CommonAssemblyAttributes.cs + $(CSC) /target:library /out:$(BIN_DIR)/$(CLR_DLL_NAME).dll /lib:$(DEP_BIN_DIR) /r:Google.Protobuf.dll /warn:0 /nologo /debug $(SRC_DIR)/com/google/ortools/util/*.cs $(GEN_DIR)/com/google/ortools/linearsolver/*.cs $(SRC_DIR)/com/google/ortools/linearsolver/*.cs $(GEN_DIR)/com/google/ortools/constraintsolver/*.cs $(SRC_DIR)/com/google/ortools/constraintsolver/*.cs $(SRC_DIR)/com/google/ortools/algorithms/*.cs $(GEN_DIR)/com/google/ortools/algorithms/*.cs $(GEN_DIR)/com/google/ortools/graph/*.cs $(GEN_DIR)/com/google/ortools/CommonAssemblyAttributes.cs $(DYNAMIC_LD) $(LDOUT)$(LIB_DIR)$S$(LIBPREFIX)$(CLR_DLL_NAME).$(DYNAMIC_SWIG_LIB_SUFFIX) $(OBJ_DIR)/swig/linear_solver_csharp_wrap.$O $(OBJ_DIR)/swig/constraint_solver_csharp_wrap.$O $(OBJ_DIR)/swig/knapsack_solver_csharp_wrap.$O $(OBJ_DIR)/swig/graph_csharp_wrap.$O $(STATIC_ALL_LNK) $(STATIC_LD_FLAGS) endif # csharp linear solver examples $(BIN_DIR)/cslinearprogramming$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/cslinearprogramming.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scslinearprogramming$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scslinearprogramming.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scslinearprogramming$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scslinearprogramming.cs $(BIN_DIR)/csintegerprogramming$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/csintegerprogramming.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsintegerprogramming$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scsintegerprogramming.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsintegerprogramming$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scsintegerprogramming.cs # csharp linear solver tests @@ -242,54 +263,54 @@ testlp: $(BIN_DIR)/testlp$(CLR_EXE_SUFFIX).exe # csharp cp examples $(BIN_DIR)/csrabbitspheasants$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/csrabbitspheasants.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsrabbitspheasants$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scsrabbitspheasants.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsrabbitspheasants$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scsrabbitspheasants.cs $(BIN_DIR)/send_more_money$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/send_more_money.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Ssend_more_money$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Ssend_more_money.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Ssend_more_money$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Ssend_more_money.cs $(BIN_DIR)/furniture_moving_intervals$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/furniture_moving_intervals.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sfurniture_moving_intervals$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Sfurniture_moving_intervals.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sfurniture_moving_intervals$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Sfurniture_moving_intervals.cs $(BIN_DIR)/organize_day_intervals$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/organize_day_intervals.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sorganize_day_intervals$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Sorganize_day_intervals.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sorganize_day_intervals$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Sorganize_day_intervals.cs $(BIN_DIR)/cstsp$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/cstsp.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scstsp$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scstsp.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scstsp$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scstsp.cs $(BIN_DIR)/cscvrptw$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/cscvrptw.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scscvrptw$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scscvrptw.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scscvrptw$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scscvrptw.cs $(BIN_DIR)/csls_api$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/csls_api.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsls_api$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scsls_api.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsls_api$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scsls_api.cs # csharp constraint solver tests $(BIN_DIR)/testcp$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/tests/testcp.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Stestcp$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Stests$Stestcp.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Stestcp$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Stests$Stestcp.cs testcp: $(BIN_DIR)/testcp$(CLR_EXE_SUFFIX).exe $(MONO) $(BIN_DIR)$Stestcp$(CLR_EXE_SUFFIX).exe $(BIN_DIR)/issue18$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/tests/issue18.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sissue18$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Stests$Sissue18.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sissue18$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Stests$Sissue18.cs issue18: $(BIN_DIR)/issue18$(CLR_EXE_SUFFIX).exe $(MONO) $(BIN_DIR)$Sissue18$(CLR_EXE_SUFFIX).exe $(BIN_DIR)/issue22$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/tests/issue22.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sissue22$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Stests$Sissue22.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sissue22$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Stests$Sissue22.cs issue22: $(BIN_DIR)/issue22$(CLR_EXE_SUFFIX).exe $(MONO) $(BIN_DIR)$Sissue22$(CLR_EXE_SUFFIX).exe $(BIN_DIR)/issue33$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/tests/issue33.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sissue33$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Stests$Sissue33.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sissue33$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Stests$Sissue33.cs issue33: $(BIN_DIR)/issue33$(CLR_EXE_SUFFIX).exe $(MONO) $(BIN_DIR)$Sissue33$(CLR_EXE_SUFFIX).exe $(BIN_DIR)/jobshop_bug$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/tests/jobshop_bug.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sjobshop_bug$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Stests$Sjobshop_bug.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Sjobshop_bug$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Stests$Sjobshop_bug.cs jobshop_bug: $(BIN_DIR)/jobshop_bug$(CLR_EXE_SUFFIX).exe $(MONO) $(BIN_DIR)$Sjobshop_bug$(CLR_EXE_SUFFIX).exe @@ -297,17 +318,17 @@ jobshop_bug: $(BIN_DIR)/jobshop_bug$(CLR_EXE_SUFFIX).exe # csharp algorithm examples $(BIN_DIR)/csknapsack$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/csknapsack.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsknapsack$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scsknapsack.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsknapsack$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scsknapsack.cs # csharp graph examples $(BIN_DIR)/csflow$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/csflow.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsflow$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Scsflow.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsflow$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Scsflow.cs # Examples using multiple libraries. $(BIN_DIR)/techtalk_scheduling$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/techtalk_scheduling.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Stechtalk_scheduling$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$Stechtalk_scheduling.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Stechtalk_scheduling$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$Stechtalk_scheduling.cs techtalk_scheduling: $(BIN_DIR)/techtalk_scheduling$(CLR_EXE_SUFFIX).exe $(MONO) $(BIN_DIR)$Stechtalk_scheduling$(CLR_EXE_SUFFIX).exe @@ -315,7 +336,7 @@ techtalk_scheduling: $(BIN_DIR)/techtalk_scheduling$(CLR_EXE_SUFFIX).exe # Build and compile custome CP examples ccs: $(BIN_DIR)/$(CLR_DLL_NAME).dll $(EX_DIR)/csharp/$(EX).cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$S$(EX)$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:$(CLR_DLL_NAME).dll $(EX_DIR)$Scsharp$S$(EX).cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$S$(EX)$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:$(CLR_DLL_NAME).dll /r:Google.Protobuf.dll $(EX_DIR)$Scsharp$S$(EX).cs rcs: ccs $(MONO) $(BIN_DIR)$S$(EX)$(CLR_EXE_SUFFIX).exe $(ARGS) @@ -347,7 +368,7 @@ else endif $(BIN_DIR)/csfz$(CLR_EXE_SUFFIX).exe: $(BIN_DIR)/Google.OrTools.Flatzinc.dll $(EX_DIR)/csharp/csfz.cs - $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsfz$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR) /r:Google.OrTools.Flatzinc.dll $(EX_DIR)$Scsharp$Scsfz.cs + $(CSC) $(SIGNING_FLAGS) /target:exe /out:$(BIN_DIR)$Scsfz$(CLR_EXE_SUFFIX).exe /platform:$(NETPLATFORM) /lib:$(BIN_DIR),$(DEP_BIN_DIR) /r:Google.OrTools.Flatzinc.dll $(EX_DIR)$Scsharp$Scsfz.cs rcsfz: $(BIN_DIR)/csfz$(CLR_EXE_SUFFIX).exe $(MONO) $(BIN_DIR)$Scsfz$(CLR_EXE_SUFFIX).exe $(ARGS) diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index f51fa83814..0014013143 100644 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -50,12 +50,29 @@ $(OBJ_DIR)/swig/linear_solver_java_wrap.$O: $(GEN_DIR)/linear_solver/linear_solv $(OBJ_DIR)/swig/graph_java_wrap.$O: $(GEN_DIR)/graph/graph_java_wrap.cc $(CCC) $(JNIFLAGS) $(JAVA_INC) -c $(GEN_DIR)$Sgraph$Sgraph_java_wrap.cc $(OBJ_OUT)$(OBJ_DIR)$Sswig$Sgraph_java_wrap.$O +$(GEN_DIR)/com/google/ortools/constraintsolver/SearchLimitProtobuf.java: $(SRC_DIR)/constraint_solver/search_limit.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --java_out=$(GEN_DIR) $(SRC_DIR)$Sconstraint_solver$Ssearch_limit.proto + +$(GEN_DIR)/com/google/ortools/constraintsolver/SolverParameters.java: $(SRC_DIR)/constraint_solver/solver_parameters.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --java_out=$(GEN_DIR) $(SRC_DIR)$Sconstraint_solver$Ssolver_parameters.proto + +$(GEN_DIR)/com/google/ortools/constraintsolver/RoutingParameters.java: $(SRC_DIR)/constraint_solver/routing_parameters.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --java_out=$(GEN_DIR) $(SRC_DIR)$Sconstraint_solver$Srouting_parameters.proto + +$(GEN_DIR)/com/google/ortools/constraintsolver/RoutingEnums.java: $(SRC_DIR)/constraint_solver/routing_enums.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(SRC_DIR) --java_out=$(GEN_DIR) $(SRC_DIR)$Sconstraint_solver$Srouting_enums.proto + $(LIB_DIR)/com.google.ortools.jar: \ + install_java_protobuf \ $(GEN_DIR)/constraint_solver/constraint_solver_java_wrap.cc \ $(GEN_DIR)/algorithms/knapsack_solver_java_wrap.cc \ $(GEN_DIR)/graph/graph_java_wrap.cc \ - $(GEN_DIR)/linear_solver/linear_solver_java_wrap.cc - $(JAVAC_BIN) -d $(OBJ_DIR) $(SRC_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Salgorithms$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Sgraph$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Slinearsolver$S*.java + $(GEN_DIR)/linear_solver/linear_solver_java_wrap.cc \ + $(GEN_DIR)/com/google/ortools/constraintsolver/SolverParameters.java \ + $(GEN_DIR)/com/google/ortools/constraintsolver/SearchLimitProtobuf.java \ + $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingParameters.java \ + $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingEnums.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp dependencies$Sinstall$Slib$Sprotobuf.jar $(SRC_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Salgorithms$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Sgraph$S*.java $(GEN_DIR)$Scom$Sgoogle$Sortools$Slinearsolver$S*.java $(JAR_BIN) cf $(LIB_DIR)$Scom.google.ortools.jar -C $(OBJ_DIR) com$Sgoogle$Sortools$S $(LIB_DIR)/$(LIBPREFIX)jniortools.$(JNI_LIB_EXT): \ @@ -71,282 +88,282 @@ $(LIB_DIR)/$(LIBPREFIX)jniortools.$(JNI_LIB_EXT): \ compile_RabbitsPheasants: $(OBJ_DIR)/com/google/ortools/samples/RabbitsPheasants.class $(OBJ_DIR)/com/google/ortools/samples/RabbitsPheasants.class: javaortools $(EX_DIR)/com/google/ortools/samples/RabbitsPheasants.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SRabbitsPheasants.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SRabbitsPheasants.java run_RabbitsPheasants: compile_RabbitsPheasants - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.RabbitsPheasants + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.RabbitsPheasants compile_LsApi: $(OBJ_DIR)/com/google/ortools/samples/LsApi.class $(OBJ_DIR)/com/google/ortools/samples/LsApi.class: javaortools $(EX_DIR)/com/google/ortools/samples/LsApi.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SLsApi.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SLsApi.java run_LsApi: compile_LsApi - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.LsApi + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.LsApi compile_GolombRuler: $(OBJ_DIR)/com/google/ortools/samples/GolombRuler.class $(OBJ_DIR)/com/google/ortools/samples/GolombRuler.class: javaortools $(EX_DIR)/com/google/ortools/samples/GolombRuler.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SGolombRuler.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SGolombRuler.java run_GolombRuler: compile_GolombRuler - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.GolombRuler + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.GolombRuler compile_Partition: $(OBJ_DIR)/com/google/ortools/samples/Partition.class $(OBJ_DIR)/com/google/ortools/samples/Partition.class: javaortools $(EX_DIR)/com/google/ortools/samples/Partition.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SPartition.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SPartition.java run_Partition: compile_Partition - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Partition + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Partition compile_SendMoreMoney: $(OBJ_DIR)/com/google/ortools/samples/SendMoreMoney.class $(OBJ_DIR)/com/google/ortools/samples/SendMoreMoney.class: javaortools $(EX_DIR)/com/google/ortools/samples/SendMoreMoney.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSendMoreMoney.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSendMoreMoney.java run_SendMoreMoney: compile_SendMoreMoney - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.SendMoreMoney + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.SendMoreMoney compile_SendMoreMoney2: $(OBJ_DIR)/com/google/ortools/samples/SendMoreMoney2.class $(OBJ_DIR)/com/google/ortools/samples/SendMoreMoney2.class: javaortools $(EX_DIR)/com/google/ortools/samples/SendMoreMoney2.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSendMoreMoney2.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSendMoreMoney2.java run_SendMoreMoney2: compile_SendMoreMoney2 - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.SendMoreMoney2 + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.SendMoreMoney2 compile_LeastDiff: $(OBJ_DIR)/com/google/ortools/samples/LeastDiff.class $(OBJ_DIR)/com/google/ortools/samples/LeastDiff.class: javaortools $(EX_DIR)/com/google/ortools/samples/LeastDiff.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SLeastDiff.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SLeastDiff.java run_LeastDiff: compile_LeastDiff - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.LeastDiff + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.LeastDiff compile_MagicSquare: $(OBJ_DIR)/com/google/ortools/samples/MagicSquare.class $(OBJ_DIR)/com/google/ortools/samples/MagicSquare.class: javaortools $(EX_DIR)/com/google/ortools/samples/MagicSquare.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMagicSquare.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMagicSquare.java run_MagicSquare: compile_MagicSquare - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.MagicSquare + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.MagicSquare compile_NQueens: $(OBJ_DIR)/com/google/ortools/samples/NQueens.class $(OBJ_DIR)/com/google/ortools/samples/NQueens.class: javaortools $(EX_DIR)/com/google/ortools/samples/NQueens.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SNQueens.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SNQueens.java run_NQueens: compile_NQueens - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.NQueens + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.NQueens compile_NQueens2: $(OBJ_DIR)/com/google/ortools/samples/NQueens2.class $(OBJ_DIR)/com/google/ortools/samples/NQueens2.class: javaortools $(EX_DIR)/com/google/ortools/samples/NQueens2.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SNQueens2.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SNQueens2.java run_NQueens2: compile_NQueens2 - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.NQueens2 + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.NQueens2 compile_AllDifferentExcept0: $(OBJ_DIR)/com/google/ortools/samples/AllDifferentExcept0.class $(OBJ_DIR)/com/google/ortools/samples/AllDifferentExcept0.class: javaortools $(EX_DIR)/com/google/ortools/samples/AllDifferentExcept0.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SAllDifferentExcept0.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SAllDifferentExcept0.java run_AllDifferentExcept0: compile_AllDifferentExcept0 - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.AllDifferentExcept0 + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.AllDifferentExcept0 compile_Diet: $(OBJ_DIR)/com/google/ortools/samples/Diet.class $(OBJ_DIR)/com/google/ortools/samples/Diet.class: javaortools $(EX_DIR)/com/google/ortools/samples/Diet.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SDiet.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SDiet.java run_Diet: compile_Diet - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Diet + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Diet compile_Map: $(OBJ_DIR)/com/google/ortools/samples/Map.class $(OBJ_DIR)/com/google/ortools/samples/Map.class: javaortools $(EX_DIR)/com/google/ortools/samples/Map.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMap.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMap.java run_Map: compile_Map - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Map + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Map compile_Map2: $(OBJ_DIR)/com/google/ortools/samples/Map2.class $(OBJ_DIR)/com/google/ortools/samples/Map2.class: javaortools $(EX_DIR)/com/google/ortools/samples/Map2.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMap2.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMap2.java run_Map2: compile_Map2 - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Map2 + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Map2 compile_Minesweeper: $(OBJ_DIR)/com/google/ortools/samples/Minesweeper.class $(OBJ_DIR)/com/google/ortools/samples/Minesweeper.class: javaortools $(EX_DIR)/com/google/ortools/samples/Minesweeper.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMinesweeper.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMinesweeper.java run_Minesweeper: compile_Minesweeper - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Minesweeper + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Minesweeper compile_QuasigroupCompletion: $(OBJ_DIR)/com/google/ortools/samples/QuasigroupCompletion.class $(OBJ_DIR)/com/google/ortools/samples/QuasigroupCompletion.class: javaortools $(EX_DIR)/com/google/ortools/samples/QuasigroupCompletion.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SQuasigroupCompletion.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SQuasigroupCompletion.java run_QuasigroupCompletion: compile_QuasigroupCompletion - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.QuasigroupCompletion + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.QuasigroupCompletion compile_SendMostMoney: $(OBJ_DIR)/com/google/ortools/samples/SendMostMoney.class $(OBJ_DIR)/com/google/ortools/samples/SendMostMoney.class: javaortools $(EX_DIR)/com/google/ortools/samples/SendMostMoney.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSendMostMoney.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSendMostMoney.java run_SendMostMoney: compile_SendMostMoney - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.SendMostMoney + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.SendMostMoney compile_Seseman: $(OBJ_DIR)/com/google/ortools/samples/Seseman.class $(OBJ_DIR)/com/google/ortools/samples/Seseman.class: javaortools $(EX_DIR)/com/google/ortools/samples/Seseman.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSeseman.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSeseman.java run_Seseman: compile_Seseman - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Seseman + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Seseman compile_Sudoku: $(OBJ_DIR)/com/google/ortools/samples/Sudoku.class $(OBJ_DIR)/com/google/ortools/samples/Sudoku.class: javaortools $(EX_DIR)/com/google/ortools/samples/Sudoku.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSudoku.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSudoku.java run_Sudoku: compile_Sudoku - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Sudoku + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Sudoku compile_Tsp: $(OBJ_DIR)/com/google/ortools/samples/Tsp.class $(OBJ_DIR)/com/google/ortools/samples/Tsp.class: javaortools $(EX_DIR)/com/google/ortools/samples/Tsp.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$STsp.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$STsp.java run_Tsp: compile_Tsp - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Tsp $(ARGS) + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Tsp $(ARGS) compile_CapacitatedVehicleRoutingProblemWithTimeWindows: $(OBJ_DIR)/com/google/ortools/samples/CapacitatedVehicleRoutingProblemWithTimeWindows.class $(OBJ_DIR)/com/google/ortools/samples/CapacitatedVehicleRoutingProblemWithTimeWindows.class: javaortools $(EX_DIR)/com/google/ortools/samples/CapacitatedVehicleRoutingProblemWithTimeWindows.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SCapacitatedVehicleRoutingProblemWithTimeWindows.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SCapacitatedVehicleRoutingProblemWithTimeWindows.java run_CapacitatedVehicleRoutingProblemWithTimeWindows: compile_CapacitatedVehicleRoutingProblemWithTimeWindows - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.CapacitatedVehicleRoutingProblemWithTimeWindows $(ARGS) + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.CapacitatedVehicleRoutingProblemWithTimeWindows $(ARGS) compile_Xkcd: $(OBJ_DIR)/com/google/ortools/samples/Xkcd.class $(OBJ_DIR)/com/google/ortools/samples/Xkcd.class: javaortools $(EX_DIR)/com/google/ortools/samples/Xkcd.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SXkcd.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SXkcd.java run_Xkcd: compile_Xkcd - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Xkcd + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Xkcd compile_SurvoPuzzle: $(OBJ_DIR)/com/google/ortools/samples/SurvoPuzzle.class $(OBJ_DIR)/com/google/ortools/samples/SurvoPuzzle.class: javaortools $(EX_DIR)/com/google/ortools/samples/SurvoPuzzle.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSurvoPuzzle.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SSurvoPuzzle.java run_SurvoPuzzle: compile_SurvoPuzzle - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.SurvoPuzzle + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.SurvoPuzzle compile_Circuit: $(OBJ_DIR)/com/google/ortools/samples/Circuit.class $(OBJ_DIR)/com/google/ortools/samples/Circuit.class: javaortools $(EX_DIR)/com/google/ortools/samples/Circuit.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SCircuit.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SCircuit.java run_Circuit: compile_Circuit - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Circuit + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Circuit compile_CoinsGrid: $(OBJ_DIR)/com/google/ortools/samples/CoinsGrid.class $(OBJ_DIR)/com/google/ortools/samples/CoinsGrid.class: javaortools $(EX_DIR)/com/google/ortools/samples/CoinsGrid.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SCoinsGrid.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SCoinsGrid.java run_CoinsGrid: compile_CoinsGrid - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.CoinsGrid + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.CoinsGrid # Java Algorithms Examples compile_Knapsack: $(OBJ_DIR)/com/google/ortools/samples/Knapsack.class $(OBJ_DIR)/com/google/ortools/samples/Knapsack.class: javaortools $(EX_DIR)/com/google/ortools/samples/Knapsack.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SKnapsack.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SKnapsack.java run_Knapsack: compile_Knapsack - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.Knapsack + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.Knapsack # Java Algorithms Examples compile_FlowExample: $(OBJ_DIR)/com/google/ortools/samples/FlowExample.class $(OBJ_DIR)/com/google/ortools/samples/FlowExample.class: javaortools $(EX_DIR)/com/google/ortools/samples/FlowExample.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SFlowExample.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SFlowExample.java run_FlowExample: compile_FlowExample javaortools - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.FlowExample + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.FlowExample compile_LinearAssignmentAPI: $(OBJ_DIR)/com/google/ortools/samples/LinearAssignmentAPI.class $(OBJ_DIR)/com/google/ortools/samples/LinearAssignmentAPI.class: javaortools $(EX_DIR)/com/google/ortools/samples/LinearAssignmentAPI.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)com$Sgoogle$Sortools$Ssamples$SLinearAssignmentAPI.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)com$Sgoogle$Sortools$Ssamples$SLinearAssignmentAPI.java run_LinearAssignmentAPI: compile_LinearAssignmentAPI javaortools - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.LinearAssignmentAPI + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.LinearAssignmentAPI # Java Linear Programming Examples compile_LinearProgramming: $(OBJ_DIR)/com/google/ortools/samples/LinearProgramming.class $(OBJ_DIR)/com/google/ortools/samples/LinearProgramming.class: javaortools $(EX_DIR)/com/google/ortools/samples/LinearProgramming.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SLinearProgramming.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SLinearProgramming.java run_LinearProgramming: compile_LinearProgramming - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.LinearProgramming + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.LinearProgramming compile_IntegerProgramming: $(OBJ_DIR)/com/google/ortools/samples/IntegerProgramming.class $(OBJ_DIR)/com/google/ortools/samples/IntegerProgramming.class: javaortools $(EX_DIR)/com/google/ortools/samples/IntegerProgramming.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SIntegerProgramming.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SIntegerProgramming.java run_IntegerProgramming: compile_IntegerProgramming - $(JAVA_BIN) -Xss2048k -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.IntegerProgramming + $(JAVA_BIN) -Xss2048k -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.IntegerProgramming # Integer programming Coin-CBC section run_MultiThreadIntegerProgramming: compile_MultiThreadIntegerProgramming - $(JAVA_BIN) -Xss2048k -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.MultiThreadTest + $(JAVA_BIN) -Xss2048k -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.MultiThreadTest compile_MultiThreadIntegerProgramming: $(OBJ_DIR)/com/google/ortools/samples/MultiThreadTest.class $(OBJ_DIR)/com/google/ortools/samples/MultiThreadTest.class: javaortools $(EX_DIR)/com/google/ortools/samples/MultiThreadTest.java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMultiThreadTest.java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$SMultiThreadTest.java # Compile and Run CP java example: $(OBJ_DIR)/com/google/ortools/samples/$(EX).class: javaortools $(EX_DIR)/com/google/ortools/samples/$(EX).java - $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$S$(EX).java + $(JAVAC_BIN) -d $(OBJ_DIR) -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar $(EX_DIR)$Scom$Sgoogle$Sortools$Ssamples$S$(EX).java cjava: $(OBJ_DIR)/com/google/ortools/samples/$(EX).class rjava: $(OBJ_DIR)/com/google/ortools/samples/$(EX).class javaortools - $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar com.google.ortools.samples.$(EX) + $(JAVA_BIN) -Djava.library.path=$(LIB_DIR) -cp $(OBJ_DIR)$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)dependencies$Sinstall$Slib$Sprotobuf.jar com.google.ortools.samples.$(EX) $(ARGS) # Build stand-alone archive file for redistribution. diff --git a/makefiles/Makefile.python.mk b/makefiles/Makefile.python.mk index 2e1f4940a8..acce9f3587 100644 --- a/makefiles/Makefile.python.mk +++ b/makefiles/Makefile.python.mk @@ -87,7 +87,7 @@ $(GEN_DIR)/ortools/constraint_solver/search_limit_pb2.py: $(SRC_DIR)/constraint_ $(COPY) $(SRC_DIR)$Sconstraint_solver$Ssearch_limit.proto $(GEN_DIR)$Sortools$Sconstraint_solver $(PROTOBUF_DIR)/bin/protoc --proto_path=$(GEN_DIR) --python_out=$(GEN_DIR) $(GEN_DIR)$Sortools$Sconstraint_solver$Ssearch_limit.proto -$(GEN_DIR)/ortools/constraint_solver/model_pb2.py: $(SRC_DIR)/constraint_solver/model.proto +$(GEN_DIR)/ortools/constraint_solver/model_pb2.py: $(SRC_DIR)/constraint_solver/model.proto $(GEN_DIR)/ortools/constraint_solver/search_limit_pb2.py $(COPY) $(SRC_DIR)$Sconstraint_solver$Smodel.proto $(GEN_DIR)$Sortools$Sconstraint_solver $(SED) -i -e "s/constraint_solver/ortools\/constraint_solver/g" $(GEN_DIR)$Sortools$Sconstraint_solver$Smodel.proto $(PROTOBUF_DIR)/bin/protoc --proto_path=$(GEN_DIR) --python_out=$(GEN_DIR) $(GEN_DIR)$Sortools$Sconstraint_solver$Smodel.proto @@ -96,6 +96,19 @@ $(GEN_DIR)/ortools/constraint_solver/assignment_pb2.py: $(SRC_DIR)/constraint_so $(COPY) $(SRC_DIR)$Sconstraint_solver$Sassignment.proto $(GEN_DIR)$Sortools$Sconstraint_solver $(PROTOBUF_DIR)/bin/protoc --proto_path=$(GEN_DIR) --python_out=$(GEN_DIR) $(GEN_DIR)$Sortools$Sconstraint_solver$Sassignment.proto +$(GEN_DIR)/ortools/constraint_solver/solver_parameters_pb2.py: $(SRC_DIR)/constraint_solver/solver_parameters.proto + $(COPY) $(SRC_DIR)$Sconstraint_solver$Ssolver_parameters.proto $(GEN_DIR)$Sortools$Sconstraint_solver + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(GEN_DIR) --python_out=$(GEN_DIR) $(GEN_DIR)$Sortools$Sconstraint_solver$Ssolver_parameters.proto + +$(GEN_DIR)/ortools/constraint_solver/routing_enums_pb2.py: $(SRC_DIR)/constraint_solver/routing_enums.proto + $(COPY) $(SRC_DIR)$Sconstraint_solver$Srouting_enums.proto $(GEN_DIR)$Sortools$Sconstraint_solver + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(GEN_DIR) --python_out=$(GEN_DIR) $(GEN_DIR)$Sortools$Sconstraint_solver$Srouting_enums.proto + +$(GEN_DIR)/ortools/constraint_solver/routing_parameters_pb2.py: $(SRC_DIR)/constraint_solver/routing_parameters.proto + $(COPY) $(SRC_DIR)$Sconstraint_solver$Srouting_parameters.proto $(GEN_DIR)$Sortools$Sconstraint_solver + $(SED) -i -e "s/constraint_solver/ortools\/constraint_solver/g" $(GEN_DIR)$Sortools$Sconstraint_solver$Srouting_parameters.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(GEN_DIR) --python_out=$(GEN_DIR) $(GEN_DIR)$Sortools$Sconstraint_solver$Srouting_parameters.proto + $(GEN_DIR)/ortools/constraint_solver/pywrapcp.py: \ $(SRC_DIR)/base/base.swig \ $(SRC_DIR)/util/python/vector.swig \ @@ -104,8 +117,11 @@ $(GEN_DIR)/ortools/constraint_solver/pywrapcp.py: \ $(SRC_DIR)/constraint_solver/constraint_solver.h \ $(SRC_DIR)/constraint_solver/constraint_solveri.h \ $(GEN_DIR)/ortools/constraint_solver/assignment_pb2.py \ - $(GEN_DIR)/ortools/constraint_solver/search_limit_pb2.py \ $(GEN_DIR)/ortools/constraint_solver/model_pb2.py \ + $(GEN_DIR)/ortools/constraint_solver/routing_enums_pb2.py \ + $(GEN_DIR)/ortools/constraint_solver/routing_parameters_pb2.py \ + $(GEN_DIR)/ortools/constraint_solver/search_limit_pb2.py \ + $(GEN_DIR)/ortools/constraint_solver/solver_parameters_pb2.py \ $(GEN_DIR)/constraint_solver/assignment.pb.h \ $(GEN_DIR)/constraint_solver/model.pb.h \ $(GEN_DIR)/constraint_solver/search_limit.pb.h diff --git a/makefiles/Makefile.third_party.unix b/makefiles/Makefile.third_party.unix index a02e0bcc3f..46d1eb8220 100644 --- a/makefiles/Makefile.third_party.unix +++ b/makefiles/Makefile.third_party.unix @@ -64,7 +64,7 @@ ACLOCAL_TARGET = \ SET_PATH = PATH=$(OR_ROOT_FULL)/dependencies/install/bin:$(PATH) # Main target. -.PHONY: makefile_third_party missing_directories +.PHONY: makefile_third_party missing_directories install_java_protobuf third_party: install_third_party makefile_third_party # Create missing directories @@ -432,6 +432,16 @@ dependencies/sources/autoconf-$(AUTOCONF_TAG)/configure: dependencies/archives/a dependencies/archives/autoconf-$(AUTOCONF_TAG).tar.gz: cd dependencies/archives && curl -OL http://ftpmirror.gnu.org/autoconf/autoconf-$(AUTOCONF_TAG).tar.gz +# Install Java protobuf + +install_java_protobuf: dependencies/install/lib/protobuf.jar + +dependencies/install/lib/protobuf.jar: dependencies/install/bin/protoc + cd dependencies/sources/protobuf-$(PROTOBUF_TAG)/java && \ + ../../../install/bin/protoc --java_out=src/main/java -I../src \ + ../src/google/protobuf/descriptor.proto + cd dependencies/sources/protobuf-$(PROTOBUF_TAG)/java/src/main/java && jar cvf ../../../../../../install/lib/protobuf.jar com/google/protobuf/*java + # Install C# protobuf CSHARP_PROTOBUF_SRC = $(shell grep Compile dependencies/sources/protobuf-$(PROTOBUF_TAG)/csharp/src/Google.Protobuf/Google.Protobuf.csproj | cut -d\" -f 2 | sed -e 's/\\/\//g') diff --git a/makefiles/Makefile.unix b/makefiles/Makefile.unix index 74df574a9f..07157a1b97 100644 --- a/makefiles/Makefile.unix +++ b/makefiles/Makefile.unix @@ -22,6 +22,8 @@ OBJ_DIR = $(OR_ROOT)objs SRC_DIR = $(OR_ROOT)src EX_DIR = $(OR_ROOT)examples INC_DIR = $(OR_ROOT)src +DEP_BIN_DIR = $(OR_ROOT)dependencies/install/bin + O = o E = LDOUT = -o # need the space. diff --git a/src/base/file.cc b/src/base/file.cc index 01b1abb680..b14e595928 100644 --- a/src/base/file.cc +++ b/src/base/file.cc @@ -143,6 +143,18 @@ bool File::Open() const { return f_ != NULL; } void File::Init() {} namespace file { +util::Status Open(const std::string& filename, const std::string& mode, + File** f, int flags) { + if (flags == Defaults()) { + *f = File::Open(filename, mode.c_str()); + if (*f != nullptr) { + return util::Status::OK; + } + } + return util::Status(util::error::INVALID_ARGUMENT, + StrCat("Could not open '", filename, "'")); +} + util::Status GetContents(const std::string& filename, std::string* output, int flags) { if (flags == Defaults()) { File* file = File::Open(filename, "r"); diff --git a/src/base/file.h b/src/base/file.h index 0d5fb3c4d2..e8490b4565 100644 --- a/src/base/file.h +++ b/src/base/file.h @@ -117,7 +117,9 @@ class File { namespace file { inline int Defaults() { return 0xBABA; } -// As of 2014-06, these methods can only be used with flags = file::Defaults(). +// As of 2016-01, these methods can only be used with flags = file::Defaults(). +util::Status Open(const std::string& filename, const std::string& mode, + File** f, int flags); util::Status SetTextProto(const std::string& filename, const google::protobuf::Message& proto, int flags); util::Status SetBinaryProto(const std::string& filename, diff --git a/src/com/google/ortools/util/ProtoHelper.cs b/src/com/google/ortools/util/ProtoHelper.cs new file mode 100644 index 0000000000..a89ed4b0d1 --- /dev/null +++ b/src/com/google/ortools/util/ProtoHelper.cs @@ -0,0 +1,30 @@ +// 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. + +namespace Google.OrTools { + +using System; +using Google.Protobuf; + +public static class ProtoHelper +{ + public static byte[] ProtoToByteArray(IMessage message) + { + int size = message.CalculateSize(); + byte[] buffer = new byte[size]; + CodedOutputStream output = new CodedOutputStream(buffer); + message.WriteTo(output); + return buffer; + } +} +} // namespace Google.OrTools diff --git a/src/constraint_solver/assignment.cc b/src/constraint_solver/assignment.cc index 1acd652f17..3960fc4c3a 100644 --- a/src/constraint_solver/assignment.cc +++ b/src/constraint_solver/assignment.cc @@ -61,7 +61,7 @@ void IntVarElement::Copy(const IntVarElement& element) { } void IntVarElement::LoadFromProto( - const IntVarAssignmentProto& int_var_assignment_proto) { + const IntVarAssignment& int_var_assignment_proto) { min_ = int_var_assignment_proto.min(); max_ = int_var_assignment_proto.max(); if (int_var_assignment_proto.active()) { @@ -87,7 +87,7 @@ bool IntVarElement::operator==(const IntVarElement& element) const { } void IntVarElement::WriteToProto( - IntVarAssignmentProto* int_var_assignment_proto) const { + IntVarAssignment* int_var_assignment_proto) const { int_var_assignment_proto->set_var_id(var_->name()); int_var_assignment_proto->set_min(min_); int_var_assignment_proto->set_max(max_); @@ -169,7 +169,7 @@ void IntervalVarElement::Restore() { } void IntervalVarElement::LoadFromProto( - const IntervalVarAssignmentProto& interval_var_assignment_proto) { + const IntervalVarAssignment& interval_var_assignment_proto) { start_min_ = interval_var_assignment_proto.start_min(); start_max_ = interval_var_assignment_proto.start_max(); duration_min_ = interval_var_assignment_proto.duration_min(); @@ -186,7 +186,7 @@ void IntervalVarElement::LoadFromProto( } void IntervalVarElement::WriteToProto( - IntervalVarAssignmentProto* interval_var_assignment_proto) const { + IntervalVarAssignment* interval_var_assignment_proto) const { interval_var_assignment_proto->set_var_id(var_->name()); interval_var_assignment_proto->set_start_min(start_min_); interval_var_assignment_proto->set_start_max(start_max_); @@ -281,7 +281,7 @@ void SequenceVarElement::Restore() { } void SequenceVarElement::LoadFromProto( - const SequenceVarAssignmentProto& sequence_var_assignment_proto) { + const SequenceVarAssignment& sequence_var_assignment_proto) { for (const int32 forward_sequence : sequence_var_assignment_proto.forward_sequence()) { forward_sequence_.push_back(forward_sequence); @@ -302,7 +302,7 @@ void SequenceVarElement::LoadFromProto( } void SequenceVarElement::WriteToProto( - SequenceVarAssignmentProto* sequence_var_assignment_proto) const { + SequenceVarAssignment* sequence_var_assignment_proto) const { sequence_var_assignment_proto->set_var_id(var_->name()); sequence_var_assignment_proto->set_active(Activated()); for (const int forward_sequence : forward_sequence_) { @@ -480,8 +480,8 @@ void LoadElement(const hash_map& id_to_element_map, } // namespace bool Assignment::Load(const std::string& filename) { - File* file = File::Open(filename, "r"); - if (file == nullptr) { + File* file; + if (!file::Open(filename, "r", &file, file::Defaults()).ok()) { LOG(INFO) << "Cannot open " << filename; return false; } @@ -526,20 +526,20 @@ void RealLoad(const AssignmentProto& assignment_proto, } void Assignment::Load(const AssignmentProto& assignment_proto) { - RealLoad( + RealLoad( assignment_proto, &int_var_container_, &AssignmentProto::int_var_assignment_size, &AssignmentProto::int_var_assignment); - RealLoad(assignment_proto, &interval_var_container_, &AssignmentProto::interval_var_assignment_size, &AssignmentProto::interval_var_assignment); - RealLoad(assignment_proto, &sequence_var_container_, &AssignmentProto::sequence_var_assignment_size, &AssignmentProto::sequence_var_assignment); if (assignment_proto.has_objective()) { - const IntVarAssignmentProto& objective = assignment_proto.objective(); + const IntVarAssignment& objective = assignment_proto.objective(); const std::string objective_id = objective.var_id(); CHECK(!objective_id.empty()); if (HasObjective() && objective_id.compare(Objective()->name()) == 0) { @@ -556,8 +556,8 @@ void Assignment::Load(const AssignmentProto& assignment_proto) { } bool Assignment::Save(const std::string& filename) const { - File* file = File::Open(filename, "w"); - if (file == nullptr) { + File* file; + if (!file::Open(filename, "w", &file, file::Defaults()).ok()) { LOG(INFO) << "Cannot open " << filename; return false; } @@ -587,20 +587,20 @@ void RealSave(AssignmentProto* const assignment_proto, void Assignment::Save(AssignmentProto* const assignment_proto) const { assignment_proto->Clear(); - RealSave( + RealSave( assignment_proto, int_var_container_, &AssignmentProto::add_int_var_assignment); - RealSave(assignment_proto, interval_var_container_, &AssignmentProto::add_interval_var_assignment); - RealSave(assignment_proto, sequence_var_container_, &AssignmentProto::add_sequence_var_assignment); if (HasObjective()) { const IntVar* objective = Objective(); const std::string& name = objective->name(); if (!name.empty()) { - IntVarAssignmentProto* objective = assignment_proto->mutable_objective(); + IntVarAssignment* objective = assignment_proto->mutable_objective(); objective->set_var_id(name); const int64 obj_min = ObjectiveMin(); const int64 obj_max = ObjectiveMax(); diff --git a/src/constraint_solver/assignment.proto b/src/constraint_solver/assignment.proto index f1feb8b4f9..a557bf01a9 100644 --- a/src/constraint_solver/assignment.proto +++ b/src/constraint_solver/assignment.proto @@ -12,54 +12,54 @@ // limitations under the License. -syntax = "proto2"; +syntax = "proto3"; package operations_research; // Storage for IntVars. -message IntVarAssignmentProto { - optional string var_id = 1; - optional int64 min = 2; - optional int64 max = 3; // if undefined -> == min. - optional bool active = 4 [default = true]; +message IntVarAssignment { + string var_id = 1; + int64 min = 2; + int64 max = 3; + bool active = 4; } // Storage for IntervalVars. -message IntervalVarAssignmentProto { - optional string var_id = 1; - optional int64 start_min = 2; - optional int64 start_max = 3; // if undefined -> == start_min. - optional int64 duration_min = 4; - optional int64 duration_max = 5; // if undefined -> == duration_min. - optional int64 end_min = 6; - optional int64 end_max = 7; // if undefined -> == end_min. - optional int64 performed_min = 8; - optional int64 performed_max = 9; // if undefined -> performed_min. - optional bool active = 10 [default = true]; +message IntervalVarAssignment { + string var_id = 1; + int64 start_min = 2; + int64 start_max = 3; + int64 duration_min = 4; + int64 duration_max = 5; + int64 end_min = 6; + int64 end_max = 7; + int64 performed_min = 8; + int64 performed_max = 9; + bool active = 10; } // Storage for SequenceVars. -message SequenceVarAssignmentProto { - optional string var_id = 1; +message SequenceVarAssignment { + string var_id = 1; repeated int32 forward_sequence = 2; repeated int32 backward_sequence = 3; repeated int32 unperformed = 4; - optional bool active = 5 [default = true]; + bool active = 5; } -// This optional message indicates how the assignment was produced. +// This message indicates how the assignment was produced. message WorkerInfo { - optional int32 worker_id = 1; - optional string bns = 2; + int32 worker_id = 1; + string bns = 2; } // Global container for all assignment variables and objective message AssignmentProto { - repeated IntVarAssignmentProto int_var_assignment = 1; - repeated IntervalVarAssignmentProto interval_var_assignment = 2; - repeated SequenceVarAssignmentProto sequence_var_assignment = 6; - optional IntVarAssignmentProto objective = 3; - optional WorkerInfo worker_info = 4; - optional bool is_valid = 5 [default = true]; + repeated IntVarAssignment int_var_assignment = 1; + repeated IntervalVarAssignment interval_var_assignment = 2; + repeated SequenceVarAssignment sequence_var_assignment = 6; + IntVarAssignment objective = 3; + WorkerInfo worker_info = 4; + bool is_valid = 5; } diff --git a/src/constraint_solver/constraint_solver.cc b/src/constraint_solver/constraint_solver.cc index 48b012cedb..e2247f90bc 100644 --- a/src/constraint_solver/constraint_solver.cc +++ b/src/constraint_solver/constraint_solver.cc @@ -39,20 +39,21 @@ #include "constraint_solver/model.pb.h" #include "util/tuple_set.h" +// These flags are used to set the fields in the DefaultSolverParameters proto. DEFINE_bool(cp_trace_propagation, false, "Trace propagation events (constraint and demon executions," " variable modifications)."); DEFINE_bool(cp_trace_search, false, "Trace search events"); -DEFINE_bool(cp_show_constraints, false, +DEFINE_bool(cp_print_added_constraints, false, "show all constraints added to the solver."); DEFINE_bool(cp_print_model, false, "use PrintModelVisitor on model before solving."); DEFINE_bool(cp_model_stats, false, "use StatisticsModelVisitor on model before solving."); -DEFINE_string(cp_export_file, "", "Export model to file using CPModelProto."); -DEFINE_bool(cp_no_solve, false, "Force failure at the beginning of a search."); +DEFINE_string(cp_export_file, "", "Export model to file using CpModel."); +DEFINE_bool(cp_disable_solve, false, + "Force failure at the beginning of a search."); DEFINE_string(cp_profile_file, "", "Export profiling overview to file."); -DEFINE_bool(cp_verbose_fail, false, "Verbose output when failing."); DEFINE_bool(cp_name_variables, false, "Force all variables to have names."); DEFINE_bool(cp_name_cast_variables, false, "Name variables casted from expressions"); @@ -65,16 +66,27 @@ void ConstraintSolverFailsHere() { VLOG(3) << "Fail"; } namespace operations_research { -// ----- SolverParameters ----- +// ----- ConstraintSolverParameters ----- -SolverParameters::SolverParameters() - : compress_trail(kDefaultTrailCompression), - trail_block_size(kDefaultTrailBlockSize), - array_split_size(kDefaultArraySplitSize), - store_names(kDefaultNameStoring), - profile_level(kDefaultProfileLevel), - trace_level(kDefaultTraceLevel), - name_all_variables(kDefaultNameAllVariables) {} +ConstraintSolverParameters Solver::DefaultSolverParameters() { + ConstraintSolverParameters params; + params.set_compress_trail(ConstraintSolverParameters::NO_COMPRESSION); + params.set_trail_block_size(8000); + params.set_array_split_size(16); + params.set_store_names(true); + params.set_profile_propagation(!FLAGS_cp_profile_file.empty()); + params.set_trace_propagation(FLAGS_cp_trace_propagation); + params.set_trace_search(FLAGS_cp_trace_search); + params.set_name_all_variables(FLAGS_cp_name_variables); + params.set_profile_file(FLAGS_cp_profile_file); + params.set_print_model(FLAGS_cp_print_model); + params.set_print_model_stats(FLAGS_cp_model_stats); + params.set_export_file(FLAGS_cp_export_file); + params.set_disable_solve(FLAGS_cp_disable_solve); + params.set_name_cast_variables(FLAGS_cp_name_cast_variables); + params.set_print_added_constraints(FLAGS_cp_print_added_constraints); + return params; +} // ----- Forward Declarations and Profiling Support ----- extern DemonProfiler* BuildDemonProfiler(Solver* const solver); @@ -89,17 +101,16 @@ bool Solver::InstrumentsDemons() const { } bool Solver::IsProfilingEnabled() const { - return parameters_.profile_level != SolverParameters::NO_PROFILING || - !FLAGS_cp_profile_file.empty(); + return parameters_.profile_propagation() || + !parameters_.profile_file().empty(); } bool Solver::InstrumentsVariables() const { - return parameters_.trace_level != SolverParameters::NO_TRACE || - FLAGS_cp_trace_propagation; + return parameters_.trace_propagation(); } bool Solver::NameAllVariables() const { - return parameters_.name_all_variables || FLAGS_cp_name_variables; + return parameters_.name_all_variables(); } // ------------------ Demon class ---------------- @@ -504,8 +515,9 @@ class ZlibTrailPacker : public TrailPacker { template class CompressedTrail { public: - CompressedTrail(int block_size, - SolverParameters::TrailCompression compression_level) + CompressedTrail( + int block_size, + ConstraintSolverParameters::TrailCompression compression_level) : block_size_(block_size), blocks_(nullptr), free_blocks_(nullptr), @@ -515,14 +527,15 @@ class CompressedTrail { current_(0), size_(0) { switch (compression_level) { - case SolverParameters::NO_COMPRESSION: { + case ConstraintSolverParameters::NO_COMPRESSION: { packer_.reset(new NoCompressionTrailPacker(block_size)); break; } - case SolverParameters::COMPRESS_WITH_ZLIB: { + case ConstraintSolverParameters::COMPRESS_WITH_ZLIB: { packer_.reset(new ZlibTrailPacker(block_size)); break; } + default: { LOG(ERROR) << "Should not be here"; } } // We zero all memory used by addrval arrays. @@ -647,7 +660,8 @@ struct Trail { std::vector rev_memory_; std::vector rev_memory_array_; - Trail(int block_size, SolverParameters::TrailCompression compression_level) + Trail(int block_size, + ConstraintSolverParameters::TrailCompression compression_level) : rev_ints_(block_size, compression_level), rev_int64s_(block_size, compression_level), rev_uint64s_(block_size, compression_level), @@ -1307,7 +1321,7 @@ extern ModelCache* BuildModelCache(Solver* const solver); std::string Solver::model_name() const { return name_; } -Solver::Solver(const std::string& name, const SolverParameters& parameters) +Solver::Solver(const std::string& name, const ConstraintSolverParameters& parameters) : name_(name), parameters_(parameters), random_(ACMRandom::DeterministicSeed()), @@ -1317,6 +1331,7 @@ Solver::Solver(const std::string& name, const SolverParameters& parameters) Solver::Solver(const std::string& name) : name_(name), + parameters_(DefaultSolverParameters()), random_(ACMRandom::DeterministicSeed()), demon_profiler_(BuildDemonProfiler(this)) { Init(); @@ -1325,7 +1340,7 @@ Solver::Solver(const std::string& name) void Solver::Init() { queue_.reset(new Queue(this)); trail_.reset( - new Trail(parameters_.trail_block_size, parameters_.compress_trail)); + new Trail(parameters_.trail_block_size(), parameters_.compress_trail())); state_ = OUTSIDE_SEARCH; branches_ = 0; fails_ = 0; @@ -1378,16 +1393,6 @@ Solver::~Solver() { DeleteBuilders(); } -const SolverParameters::TrailCompression -SolverParameters::kDefaultTrailCompression = SolverParameters::NO_COMPRESSION; -const int SolverParameters::kDefaultTrailBlockSize = 8000; -const int SolverParameters::kDefaultArraySplitSize = 16; -const bool SolverParameters::kDefaultNameStoring = true; -const SolverParameters::ProfileLevel SolverParameters::kDefaultProfileLevel = - SolverParameters::NO_PROFILING; -const SolverParameters::TraceLevel SolverParameters::kDefaultTraceLevel = - SolverParameters::NO_TRACE; -const bool SolverParameters::kDefaultNameAllVariables = false; std::string Solver::DebugString() const { std::string out = "Solver(name = \"" + name_ + "\", state = "; @@ -1552,7 +1557,7 @@ void Solver::AddConstraint(Constraint* const c) { additional_constraints_list_.push_back(c); additional_constraints_parent_list_.push_back(constraint_parent); } else { - if (FLAGS_cp_show_constraints) { + if (parameters_.print_added_constraints()) { LOG(INFO) << c->DebugString(); } constraints_list_.push_back(c); @@ -1605,20 +1610,21 @@ void Solver::Accept(ModelVisitor* const visitor, void Solver::ProcessConstraints() { // Both constraints_list_ and additional_constraints_list_ are used in // a FIFO way. - if (FLAGS_cp_print_model) { + if (parameters_.print_model()) { ModelVisitor* const visitor = MakePrintModelVisitor(); Accept(visitor); } - if (FLAGS_cp_model_stats) { + if (parameters_.print_model_stats()) { ModelVisitor* const visitor = MakeStatisticsModelVisitor(); Accept(visitor); } - if (!FLAGS_cp_export_file.empty()) { - File* file = File::Open(FLAGS_cp_export_file, "wb"); - if (file == nullptr) { - LOG(WARNING) << "Cannot open " << FLAGS_cp_export_file; + const std::string export_file = parameters_.export_file(); + if (!export_file.empty()) { + File* file; + if (!file::Open(export_file, "wb", &file, file::Defaults()).ok()) { + LOG(WARNING) << "Cannot open " << export_file; } else { - CPModelProto export_proto; + CpModel export_proto; ExportModel(&export_proto); VLOG(1) << export_proto.DebugString(); RecordWriter writer(file); @@ -1627,7 +1633,7 @@ void Solver::ProcessConstraints() { } } - if (FLAGS_cp_no_solve) { + if (parameters_.disable_solve()) { LOG(INFO) << "Forcing early failure"; Fail(); } @@ -1819,11 +1825,10 @@ void Solver::NewSearch(DecisionBuilder* const db, } } else { // Top level search print_trace_ = nullptr; // Clears it first. - if (FLAGS_cp_trace_propagation || - parameters_.trace_level != SolverParameters::NO_TRACE) { + if (parameters_.trace_propagation()) { print_trace_ = BuildPrintTrace(this); print_trace_->Install(); - } else if (FLAGS_cp_trace_search) { + } else if (parameters_.trace_search()) { // This is useful to trace the exact behavior of the search. // The '######## ' prefix is the same as the progagation trace. // Search trace is subsumed by propagation trace, thus only one @@ -2192,9 +2197,10 @@ void Solver::EndSearch() { // Restores the state. state_ = OUTSIDE_SEARCH; // Checks if we want to export the profile info. - if (!FLAGS_cp_profile_file.empty()) { - LOG(INFO) << "Exporting profile to " << FLAGS_cp_profile_file; - ExportProfilingOverview(FLAGS_cp_profile_file); + if (!parameters_.profile_file().empty()) { + const std::string& file_name = parameters_.profile_file(); + LOG(INFO) << "Exporting profile to " << file_name; + ExportProfilingOverview(file_name); } } else { // We clean the nested Search. delete search; @@ -2366,7 +2372,7 @@ std::string Solver::GetName(const PropagationBaseObject* object) { if (cast_info != nullptr && cast_info->expression != nullptr) { if (cast_info->expression->HasName()) { return StringPrintf("Var<%s>", cast_info->expression->name().c_str()); - } else if (FLAGS_cp_name_cast_variables) { + } else if (parameters_.name_cast_variables()) { return StringPrintf("Var<%s>", cast_info->expression->DebugString().c_str()); } else { @@ -2377,7 +2383,7 @@ std::string Solver::GetName(const PropagationBaseObject* object) { } } const std::string base_name = object->BaseName(); - if (FLAGS_cp_name_variables && !base_name.empty()) { + if (parameters_.name_all_variables() && !base_name.empty()) { const std::string new_name = StringPrintf("%s_%d", base_name.c_str(), anonymous_variable_index_++); propagation_object_names_[object] = new_name; @@ -2387,7 +2393,7 @@ std::string Solver::GetName(const PropagationBaseObject* object) { } void Solver::SetName(const PropagationBaseObject* object, const std::string& name) { - if (parameters_.store_names && + if (parameters_.store_names() && GetName(object).compare(name) != 0) { // in particular if name.empty() propagation_object_names_[object] = name; } @@ -2396,7 +2402,7 @@ void Solver::SetName(const PropagationBaseObject* object, const std::string& nam bool Solver::HasName(const PropagationBaseObject* const object) const { return ContainsKey(propagation_object_names_, const_cast(object)) || - (!object->BaseName().empty() && FLAGS_cp_name_variables); + (!object->BaseName().empty() && parameters_.name_all_variables()); } // ------------------ Useful Operators ------------------ diff --git a/src/constraint_solver/constraint_solver.h b/src/constraint_solver/constraint_solver.h index eb8fc4333a..08135cc2fc 100644 --- a/src/constraint_solver/constraint_solver.h +++ b/src/constraint_solver/constraint_solver.h @@ -80,8 +80,9 @@ #include "base/timer.h" #include "base/map_util.h" #include "base/hash.h" -#include "base/random.h" +#include "constraint_solver/solver_parameters.pb.h" #include "util/tuple_set.h" +#include "base/random.h" class File; @@ -91,13 +92,13 @@ namespace operations_research { class Assignment; class AssignmentProto; class BaseObject; -class CPArgumentProto; -class CPConstraintProto; -class CPIntegerExpressionProto; -class CPIntervalVariableProto; -class CPModelLoader; -class CPModelProto; -class CPSequenceVariableProto; +class CpArgument; +class CpConstraint; +class CpIntegerExpression; +class CpIntervalVariable; +class CpModelLoader; +class CpModel; +class CpSequenceVariable; class CastConstraint; class Constraint; class Decision; @@ -112,10 +113,10 @@ class ExpressionCache; class IntExpr; class IntTupleSet; class IntVar; -class IntVarAssignmentProto; +class IntVarAssignment; class IntVarElement; class IntervalVar; -class IntervalVarAssignmentProto; +class IntervalVarAssignment; class IntervalVarElement; class LocalSearchFilter; class LocalSearchOperator; @@ -133,72 +134,20 @@ class RevBitMatrix; class RevBitSet; class Search; class SearchLimit; -class SearchLimitProto; +class SearchLimitParameters; class SearchMonitor; class SequenceVar; -class SequenceVarAssignmentProto; +class SequenceVarAssignment; class SolutionCollector; class SolutionPool; class Solver; +class ConstraintSolverParameters; class SymmetryBreaker; struct StateInfo; struct Trail; template class SimpleRevFIFO; -// This struct holds all parameters for the Solver object. -// SolverParameters is only used by the Solver constructor to define solving -// parameters such as the trail compression or the profile level. -// Note this is for advanced users only. -struct SolverParameters { - public: - enum TrailCompression { - NO_COMPRESSION, COMPRESS_WITH_ZLIB - }; - - enum ProfileLevel { NO_PROFILING, NORMAL_PROFILING }; - - enum TraceLevel { NO_TRACE, NORMAL_TRACE }; - - static const TrailCompression kDefaultTrailCompression; - static const int kDefaultTrailBlockSize; - static const int kDefaultArraySplitSize; - static const bool kDefaultNameStoring; - static const ProfileLevel kDefaultProfileLevel; - static const TraceLevel kDefaultTraceLevel; - static const bool kDefaultNameAllVariables; - - SolverParameters(); - - // This parameter indicates if the solver should compress the trail - // during the search. No compression means that the solver will be faster, - // but will use more memory. - TrailCompression compress_trail; - - // This parameter indicates the default size of a block of the trail. - // Compression applies at the block level. - int trail_block_size; - - // When a sum/min/max operation is applied on a large array, this - // array is recursively split into blocks of size 'array_split_size'. - int array_split_size; - - // This parameters indicates if the solver should store the names of - // the objets it manages. - bool store_names; - - // Support for profiling propagation. LIGHT supports only a reduced - // version of the summary. COMPLETE supports the full version of the - // summary, as well as the csv export. - ProfileLevel profile_level; - - // Support for full trace of propagation. - TraceLevel trace_level; - - // Should anonymous variables be given a name. - bool name_all_variables; -}; - // This struct holds all parameters for the default search. // DefaultPhaseParameters is only used by Solver::MakeDefaultPhase methods. // Note this is for advanced users only. @@ -817,24 +766,25 @@ class Solver { typedef std::function Closure; #ifndef SWIG - typedef std::function + typedef std::function IntegerExpressionBuilder; - typedef std::function + typedef std::function ConstraintBuilder; - typedef std::function IntervalVariableBuilder; - typedef std::function SequenceVariableBuilder; + 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(const std::string& modelname, const ConstraintSolverParameters& parameters); ~Solver(); - // Read-only Parameters. - const SolverParameters& parameters() const { return parameters_; } + // Stored Parameters. + ConstraintSolverParameters parameters() const { return parameters_; } + // Create a ConstraintSolverParameters proto with all the default values. + static ConstraintSolverParameters DefaultSolverParameters(); // reversibility @@ -1026,22 +976,22 @@ class Solver { // Exports the model to protobuf. This code will be called // from inside the solver during the start of the search. - void ExportModel(CPModelProto* const proto) const; + void ExportModel(CpModel* const proto) const; // Exports the model to protobuf. Search monitors are useful to pass // the objective and limits to the protobuf. void ExportModel(const std::vector& monitors, - CPModelProto* const proto) const; + CpModel* const proto) const; // Exports the model to protobuf. Search monitors are useful to pass // the objective and limits to the protobuf. - void ExportModel(const std::vector& monitors, - CPModelProto* const proto, DecisionBuilder* const db) const; + void ExportModel(const std::vector& monitors, CpModel* const proto, + DecisionBuilder* const db) const; // Loads the model into the solver, and returns true upon success. - bool LoadModel(const CPModelProto& proto); + bool LoadModel(const CpModel& proto); // Loads the model into the solver, appends search monitors to monitors, // and returns true upon success. - bool LoadModel(const CPModelProto& proto, std::vector* monitors); + bool LoadModel(const CpModel& proto, std::vector* monitors); // Upgrades the model to the latest version. - static bool UpgradeModel(CPModelProto* const proto); + static bool UpgradeModel(CpModel* const proto); #if !defined(SWIG) // Collects decision variables. @@ -2239,7 +2189,10 @@ class Solver { int64 solutions, bool smart_time_check, bool cumulative); // Creates a search limit from its protobuf description - SearchLimit* MakeLimit(const SearchLimitProto& proto); + SearchLimit* MakeLimit(const SearchLimitParameters& proto); + + // Creates a search limit proto containing default values. + SearchLimitParameters MakeDefaultSearchLimitParameters() const; // Creates a search limit that is reached when either of the underlying limit // is reached. That is, the returned limit is more stringent than both @@ -2845,7 +2798,7 @@ class Solver { // Exports the profiling information in a human readable overview. // The parameter profile_level used to create the solver must be - // different from NO_PROFILING. + // set to true. void ExportProfilingOverview(const std::string& filename); // Returns true whether the current search has been @@ -2925,12 +2878,13 @@ class Solver { friend class SearchMonitor; friend class SearchLimit; -#ifndef SWIG +#if !defined(SWIG) friend void InternalSaveBooleanVarValue(Solver* const, IntVar* const); template friend class SimpleRevFIFO; template friend class RevImmutableMultiMap; + // 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 @@ -3059,7 +3013,7 @@ class Solver { IntExpr** const right); const std::string name_; - const SolverParameters parameters_; + const ConstraintSolverParameters parameters_; hash_map propagation_object_names_; hash_map cast_information_; hash_set cast_constraints_; @@ -4598,8 +4552,8 @@ class IntVarElement : public AssignmentElement { var_->SetRange(min_, max_); } } - void LoadFromProto(const IntVarAssignmentProto& int_var_assignment_proto); - void WriteToProto(IntVarAssignmentProto* int_var_assignment_proto) const; + void LoadFromProto(const IntVarAssignment& int_var_assignment_proto); + void WriteToProto(IntVarAssignment* int_var_assignment_proto) const; int64 Min() const { return min_; } void SetMin(int64 m) { min_ = m; } @@ -4645,9 +4599,8 @@ class IntervalVarElement : public AssignmentElement { void Store(); void Restore(); void LoadFromProto( - const IntervalVarAssignmentProto& interval_var_assignment_proto); - void WriteToProto( - IntervalVarAssignmentProto* interval_var_assignment_proto) const; + const IntervalVarAssignment& interval_var_assignment_proto); + void WriteToProto(IntervalVarAssignment* interval_var_assignment_proto) const; int64 StartMin() const { return start_min_; } int64 StartMax() const { return start_max_; } @@ -4757,9 +4710,8 @@ class SequenceVarElement : public AssignmentElement { void Store(); void Restore(); void LoadFromProto( - const SequenceVarAssignmentProto& sequence_var_assignment_proto); - void WriteToProto( - SequenceVarAssignmentProto* sequence_var_assignment_proto) const; + const SequenceVarAssignment& sequence_var_assignment_proto); + void WriteToProto(SequenceVarAssignment* sequence_var_assignment_proto) const; const std::vector& ForwardSequence() const; const std::vector& BackwardSequence() const; diff --git a/src/constraint_solver/constraint_solveri.h b/src/constraint_solver/constraint_solveri.h index 39c52cd873..e3e4a3b2d2 100644 --- a/src/constraint_solver/constraint_solveri.h +++ b/src/constraint_solver/constraint_solveri.h @@ -1152,7 +1152,7 @@ class BaseLns : public IntVarLocalSearchOperator { explicit BaseLns(const std::vector& vars); ~BaseLns() override; virtual void InitFragments(); - virtual bool NextFragment(); + virtual bool NextFragment() = 0; void AppendToFragment(int index); int FragmentSize() const; diff --git a/src/constraint_solver/csharp/constraint_solver.swig b/src/constraint_solver/csharp/constraint_solver.swig index 61b1dad749..911fce21ba 100644 --- a/src/constraint_solver/csharp/constraint_solver.swig +++ b/src/constraint_solver/csharp/constraint_solver.swig @@ -23,10 +23,24 @@ using System.Collections; %include "std_vector.i" %include "util/csharp/tuple_set.swig" %include "util/csharp/functions.swig" +%include "util/csharp/proto.swig" + +// We need to forward-declare the proto here, so that PROTO_INPUT involving it +// works correctly. The order matters very much: this declaration needs to be +// before the %{ #include ".../constraint_solver.h" %}. +namespace operations_research { +class ConstraintSolverParameters; +class SearchLimitParameters; +} // namespace operations_research %module(directors="1", allprotected="1") operations_research; #pragma SWIG nowarn=473 +%typemap(javaimports) operations_research::Solver %{ +import com.google.ortools.constraintsolver.ConstraintSolverParameters; +import com.google.ortools.constraintsolver.SearchLimitParameters; +%} + %feature("director") Action; %feature("director") BaseLns; %feature("director") Decision; @@ -39,7 +53,6 @@ using System.Collections; %feature("director") SearchMonitor; %feature("director") SequenceVarLocalSearchOperator; %feature("director") SymmetryBreaker; - %{ #include @@ -51,6 +64,8 @@ using System.Collections; #include "constraint_solver/constraint_solver.h" #include "constraint_solver/constraint_solveri.h" #include "util/functions_swig_helpers.h" +#include "constraint_solver/search_limit.pb.h" +#include "constraint_solver/solver_parameters.pb.h" namespace operations_research { class LocalSearchPhaseParameters { @@ -837,6 +852,19 @@ class LocalSearchPhaseParameters { }; } // namespace operations_research +// Protobuf support +PROTO_INPUT(operations_research::ConstraintSolverParameters, + Google.OrTools.ConstraintSolver.ConstraintSolverParameters, + parameters) +PROTO2_RETURN(operations_research::ConstraintSolverParameters, + Google.OrTools.ConstraintSolver.ConstraintSolverParameters) + +PROTO_INPUT(operations_research::SearchLimitParameters, + Google.OrTools.ConstraintSolver.SearchLimitParameters, + proto) +PROTO2_RETURN(operations_research::SearchLimitParameters, + Google.OrTools.ConstraintSolver.SearchLimitParameters) + // Wrap cp includes %include "constraint_solver/constraint_solver.h" %include "constraint_solver/constraint_solveri.h" diff --git a/src/constraint_solver/csharp/routing.swig b/src/constraint_solver/csharp/routing.swig index f28c8d35ec..cfc36f831e 100644 --- a/src/constraint_solver/csharp/routing.swig +++ b/src/constraint_solver/csharp/routing.swig @@ -15,6 +15,14 @@ %include "constraint_solver/csharp/constraint_solver.swig" +// We need to forward-declare the proto here, so that PROTO_INPUT involving it +// works correctly. The order matters very much: this declaration needs to be +// before the %{ #include ".../routing.h" %}. +namespace operations_research { +class RoutingModelParameters; +class RoutingSearchParameters; +} // namespace operations_research + // Include the file we want to wrap a first time. %{ #include "constraint_solver/routing.h" @@ -111,7 +119,7 @@ CS_TYPEMAP_STDVECTOR_IN1(operations_research::RoutingModel::NodeIndex, int, int) // Create input mapping for NodeEvaluator2 and LongResultCallbackN // Callback wrapping. -// TODO(viger): split out the callback code; it creates another file since +// TODO(user): split out the callback code; it creates another file since // it uses a different module. %{ class LongResultCallback1 { @@ -260,5 +268,17 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 %rename("%(camelcase)s", %$isfunction) ""; +// Protobuf support +PROTO_INPUT(operations_research::RoutingSearchParameters, + Google.OrTools.ConstraintSolver.RoutingSearchParameters, + search_parameters) +PROTO_INPUT(operations_research::RoutingModelParameters, + Google.OrTools.ConstraintSolver.RoutingModelParameters, + parameters) +PROTO2_RETURN(operations_research::RoutingSearchParameters, + Google.OrTools.ConstraintSolver.RoutingSearchParameters) +PROTO2_RETURN(operations_research::RoutingModelParameters, + Google.OrTools.ConstraintSolver.RoutingModelParameters) + // Wrap cp includes %include constraint_solver/routing.h diff --git a/src/constraint_solver/demon_profiler.cc b/src/constraint_solver/demon_profiler.cc index a857c3c6d1..65603736e0 100644 --- a/src/constraint_solver/demon_profiler.cc +++ b/src/constraint_solver/demon_profiler.cc @@ -260,10 +260,10 @@ class DemonProfiler : public PropagationMonitor { " --- Demon: %s\n invocations=%" GG_LL_FORMAT "d, failures=%" GG_LL_FORMAT "d, total runtime=%" GG_LL_FORMAT "d us, [average=%.2lf, median=%.2lf, stddev=%.2lf]\n"; - File* const file = File::Open(filename, "w"); + File* file; const std::string model = StringPrintf("Model %s:\n", solver->model_name().c_str()); - if (file) { + if (file::Open(filename, "w", &file, file::Defaults()).ok()) { file::WriteString(file, model, file::Defaults()).IgnoreError(); std::vector to_sort; for (hash_map::const_iterator it = @@ -319,7 +319,7 @@ class DemonProfiler : public PropagationMonitor { } } } - file->Close(); + file->Close(file::Defaults()).IgnoreError(); } // Export Information diff --git a/src/constraint_solver/demon_profiler.proto b/src/constraint_solver/demon_profiler.proto index 34f5044cda..59dc1ce4ab 100644 --- a/src/constraint_solver/demon_profiler.proto +++ b/src/constraint_solver/demon_profiler.proto @@ -12,21 +12,21 @@ // limitations under the License. -syntax = "proto2"; +syntax = "proto3"; package operations_research; message DemonRuns { - required string demon_id = 1; + string demon_id = 1; repeated int64 start_time = 2; repeated int64 end_time = 3; - required int64 failures = 4; + int64 failures = 4; } message ConstraintRuns { - required string constraint_id = 1; + string constraint_id = 1; repeated int64 initial_propagation_start_time = 2; repeated int64 initial_propagation_end_time = 3; - required int64 failures = 4; + int64 failures = 4; repeated DemonRuns demons = 5; } diff --git a/src/constraint_solver/expr_array.cc b/src/constraint_solver/expr_array.cc index c98b6141c9..768f3f5d75 100644 --- a/src/constraint_solver/expr_array.cc +++ b/src/constraint_solver/expr_array.cc @@ -40,7 +40,7 @@ class TreeArrayConstraint : public CastConstraint { IntVar* const sum_var) : CastConstraint(solver, sum_var), vars_(vars), - block_size_(solver->parameters().array_split_size) { + block_size_(solver->parameters().array_split_size()) { std::vector lengths; lengths.push_back(vars_.size()); while (lengths.back() > 1) { @@ -3046,7 +3046,7 @@ IntExpr* MakeSumArrayAux(Solver* const solver, const std::vector& vars, if (AreAllBooleans(vars)) { solver->AddConstraint( solver->RevAlloc(new SumBooleanEqualToVar(solver, vars, sum_var))); - } else if (size <= solver->parameters().array_split_size) { + } else if (size <= solver->parameters().array_split_size()) { solver->AddConstraint( solver->RevAlloc(new SmallSumConstraint(solver, vars, sum_var))); } else { @@ -3287,7 +3287,7 @@ IntExpr* Solver::MakeMin(const std::vector& vars) { new_max = std::min(new_max, vars[i]->Max()); } IntVar* const new_var = MakeIntVar(new_min, new_max); - if (size <= parameters_.array_split_size) { + if (size <= parameters_.array_split_size()) { AddConstraint(RevAlloc(new SmallMinConstraint(this, vars, new_var))); } else { AddConstraint(RevAlloc(new MinConstraint(this, vars, new_var))); @@ -3328,7 +3328,7 @@ IntExpr* Solver::MakeMax(const std::vector& vars) { new_max = std::max(new_max, vars[i]->Max()); } IntVar* const new_var = MakeIntVar(new_min, new_max); - if (size <= parameters_.array_split_size) { + if (size <= parameters_.array_split_size()) { AddConstraint(RevAlloc(new SmallMaxConstraint(this, vars, new_var))); } else { AddConstraint(RevAlloc(new MaxConstraint(this, vars, new_var))); @@ -3347,7 +3347,7 @@ Constraint* Solver::MakeMinEquality(const std::vector& vars, if (size > 2) { if (AreAllBooleans(vars)) { return RevAlloc(new ArrayBoolAndEq(this, vars, min_var)); - } else if (size <= parameters_.array_split_size) { + } else if (size <= parameters_.array_split_size()) { return RevAlloc(new SmallMinConstraint(this, vars, min_var)); } else { return RevAlloc(new MinConstraint(this, vars, min_var)); @@ -3367,7 +3367,7 @@ Constraint* Solver::MakeMaxEquality(const std::vector& vars, if (size > 2) { if (AreAllBooleans(vars)) { return RevAlloc(new ArrayBoolOrEq(this, vars, max_var)); - } else if (size <= parameters_.array_split_size) { + } else if (size <= parameters_.array_split_size()) { return RevAlloc(new SmallMaxConstraint(this, vars, max_var)); } else { return RevAlloc(new MaxConstraint(this, vars, max_var)); @@ -3421,7 +3421,7 @@ Constraint* Solver::MakeSumEquality(const std::vector& vars, int64 cst) } if (DetectSumOverflow(vars)) { return RevAlloc(new SafeSumConstraint(this, vars, MakeIntConst(cst))); - } else if (size <= parameters_.array_split_size) { + } else if (size <= parameters_.array_split_size()) { return RevAlloc(new SmallSumConstraint(this, vars, MakeIntConst(cst))); } else { return RevAlloc(new SumConstraint(this, vars, MakeIntConst(cst))); @@ -3446,7 +3446,7 @@ Constraint* Solver::MakeSumEquality(const std::vector& vars, } else { if (DetectSumOverflow(vars)) { return RevAlloc(new SafeSumConstraint(this, vars, var)); - } else if (size <= parameters_.array_split_size) { + } else if (size <= parameters_.array_split_size()) { return RevAlloc(new SmallSumConstraint(this, vars, var)); } else { return RevAlloc(new SumConstraint(this, vars, var)); diff --git a/src/constraint_solver/io.cc b/src/constraint_solver/io.cc index 43126f373c..8f154fe1d5 100644 --- a/src/constraint_solver/io.cc +++ b/src/constraint_solver/io.cc @@ -35,60 +35,60 @@ #include "util/vector_map.h" namespace operations_research { -// ---------- CPModelLoader ----------- +// ---------- CpModelLoader ----------- -// The class CPModelLoader is responsible for reading a protocol +// The class CpModelLoader is responsible for reading a protocol // buffer representing a CP model and creating the corresponding CP // model with the expressions and constraints. It should not be used directly. -class CPModelLoader { +class CpModelLoader { public: - explicit CPModelLoader(Solver* const solver) : solver_(solver) {} - ~CPModelLoader() {} + explicit CpModelLoader(Solver* const solver) : solver_(solver) {} + ~CpModelLoader() {} Solver* solver() const { return solver_; } // Builds integer expression from proto and stores it. It returns // true upon success. - bool BuildFromProto(const CPIntegerExpressionProto& proto); + bool BuildFromProto(const CpIntegerExpression& proto); // Builds constraint from proto and returns it. - Constraint* BuildFromProto(const CPConstraintProto& proto); + Constraint* BuildFromProto(const CpConstraint& proto); // Builds interval variable from proto and stores it. It returns // true upon success. - bool BuildFromProto(const CPIntervalVariableProto& proto); + bool BuildFromProto(const CpIntervalVariable& proto); // Builds sequence variable from proto and stores it. It returns // true upon success. - bool BuildFromProto(const CPSequenceVariableProto& proto); + bool BuildFromProto(const CpSequenceVariable& proto); // Returns stored integer expression. IntExpr* IntegerExpression(int index) const; // Returns stored interval variable. IntervalVar* IntervalVariable(int index) const; - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, int64* to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, IntExpr** to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, IntTupleSet* to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, IntervalVar** to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, SequenceVar** to_fill); - bool ScanOneArgument(int type_index, const CPArgumentProto& arg_proto, + bool ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill); template @@ -325,24 +325,25 @@ class ArgumentHolder { template void ExportToProto(VectorMap* const tags, P* const proto) const { for (const auto& it : integer_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); arg_proto->set_integer_value(it.second); + arg_proto->set_type(CpArgument::INTEGER_VALUE); } for (const auto& it : integer_array_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); for (int64 value : it.second) { arg_proto->add_integer_array(value); } + arg_proto->set_type(CpArgument::INTEGER_ARRAY); } for (const auto& it : integer_matrix_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); - CPIntegerMatrixProto* const matrix_proto = - arg_proto->mutable_integer_matrix(); + CpIntegerMatrix* const matrix_proto = arg_proto->mutable_integer_matrix(); const int columns = it.second.first; CHECK_GT(columns, 0); const int rows = it.second.second.size() / columns; @@ -351,48 +352,55 @@ class ArgumentHolder { for (int64 value : it.second.second) { matrix_proto->add_values(value); } + arg_proto->set_type(CpArgument::INTEGER_MATRIX); } for (const auto& it : integer_expression_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); arg_proto->set_integer_expression_index(it.second); + arg_proto->set_type(CpArgument::EXPRESSION); } for (const auto& it : integer_variable_array_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); for (int expr : it.second) { arg_proto->add_integer_expression_array(expr); } + arg_proto->set_type(CpArgument::EXPRESSION_ARRAY); } for (const auto& it : interval_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); arg_proto->set_interval_index(it.second); + arg_proto->set_type(CpArgument::INTERVAL); } for (const auto& it : interval_array_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); for (int arg : it.second) { arg_proto->add_interval_array(arg); } + arg_proto->set_type(CpArgument::INTERVAL_ARRAY); } for (const auto& it : sequence_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); arg_proto->set_sequence_index(it.second); + arg_proto->set_type(CpArgument::SEQUENCE); } for (const auto& it : sequence_array_argument_) { - CPArgumentProto* const arg_proto = proto->add_arguments(); + CpArgument* const arg_proto = proto->add_arguments(); arg_proto->set_argument_index(tags->Add(it.first)); for (int arg : it.second) { arg_proto->add_sequence_array(arg); } + arg_proto->set_type(CpArgument::SEQUENCE_ARRAY); } } @@ -487,12 +495,12 @@ class ArgumentHolder { static const int kModelVersion = 1; // The second pass visitor will visited sorted expressions, interval -// vars and expressions and export them to a CPModelProto protocol +// vars and expressions and export them to a CpModel protocol // buffer. class SecondPassVisitor : public ModelVisitor { public: SecondPassVisitor(const FirstPassVisitor& first_pass, - CPModelProto* const model_proto) + CpModel* const model_proto) : expression_map_(first_pass.expression_map()), interval_map_(first_pass.interval_map()), sequence_map_(first_pass.sequence_map()), @@ -548,7 +556,7 @@ class SecondPassVisitor : public ModelVisitor { } const int index = model_proto_->constraints_size(); - CPConstraintProto* const constraint_proto = model_proto_->add_constraints(); + CpConstraint* const constraint_proto = model_proto_->add_constraints(); ExportToProto(constraint, constraint_proto, type_name, index); if (constraint->HasName()) { constraint_proto->set_name(constraint->name()); @@ -564,7 +572,7 @@ class SecondPassVisitor : public ModelVisitor { void EndVisitIntegerExpression(const std::string& type_name, const IntExpr* const expression) override { const int index = model_proto_->expressions_size(); - CPIntegerExpressionProto* const expression_proto = + CpIntegerExpression* const expression_proto = model_proto_->add_expressions(); ExportToProto(expression, expression_proto, type_name, index); PopArgumentHolder(); @@ -642,19 +650,18 @@ class SecondPassVisitor : public ModelVisitor { IntExpr* const delegate) override { if (delegate != nullptr) { const int index = model_proto_->expressions_size(); - CPIntegerExpressionProto* const var_proto = - model_proto_->add_expressions(); + CpIntegerExpression* const var_proto = model_proto_->add_expressions(); var_proto->set_index(index); var_proto->set_type_index(TagIndex(ModelVisitor::kIntegerVariable)); - CPArgumentProto* const sub_proto = var_proto->add_arguments(); + CpArgument* const sub_proto = var_proto->add_arguments(); sub_proto->set_argument_index( TagIndex(ModelVisitor::kExpressionArgument)); sub_proto->set_integer_expression_index( FindExpressionIndexOrDie(delegate)); + sub_proto->set_type(CpArgument::EXPRESSION); } else { const int index = model_proto_->expressions_size(); - CPIntegerExpressionProto* const var_proto = - model_proto_->add_expressions(); + CpIntegerExpression* const var_proto = model_proto_->add_expressions(); var_proto->set_index(index); var_proto->set_type_index(TagIndex(ModelVisitor::kIntegerVariable)); if (variable->HasName()) { @@ -662,21 +669,24 @@ class SecondPassVisitor : public ModelVisitor { } if (variable->Size() == variable->Max() - variable->Min() + 1) { // Contiguous - CPArgumentProto* const min_proto = var_proto->add_arguments(); + CpArgument* const min_proto = var_proto->add_arguments(); min_proto->set_argument_index(TagIndex(ModelVisitor::kMinArgument)); min_proto->set_integer_value(variable->Min()); - CPArgumentProto* const max_proto = var_proto->add_arguments(); + min_proto->set_type(CpArgument::INTEGER_VALUE); + CpArgument* const max_proto = var_proto->add_arguments(); max_proto->set_argument_index(TagIndex(ModelVisitor::kMaxArgument)); max_proto->set_integer_value(variable->Max()); + max_proto->set_type(CpArgument::INTEGER_VALUE); } else { // Non Contiguous - CPArgumentProto* const values_proto = var_proto->add_arguments(); + CpArgument* const values_proto = var_proto->add_arguments(); values_proto->set_argument_index( TagIndex(ModelVisitor::kValuesArgument)); std::unique_ptr it(variable->MakeDomainIterator(false)); for (const int64 value : InitAndGetValues(it.get())) { values_proto->add_integer_array(value); } + values_proto->set_type(CpArgument::INTEGER_ARRAY); } } } @@ -685,15 +695,17 @@ class SecondPassVisitor : public ModelVisitor { const std::string& operation, int64 value, IntVar* const delegate) override { const int index = model_proto_->expressions_size(); - CPIntegerExpressionProto* const var_proto = model_proto_->add_expressions(); + CpIntegerExpression* const var_proto = model_proto_->add_expressions(); var_proto->set_index(index); var_proto->set_type_index(TagIndex(ModelVisitor::kIntegerVariable)); - CPArgumentProto* const sub_proto = var_proto->add_arguments(); + CpArgument* const sub_proto = var_proto->add_arguments(); sub_proto->set_argument_index(TagIndex(ModelVisitor::kVariableArgument)); sub_proto->set_integer_expression_index(FindExpressionIndexOrDie(delegate)); - CPArgumentProto* const value_proto = var_proto->add_arguments(); + sub_proto->set_type(CpArgument::EXPRESSION); + CpArgument* const value_proto = var_proto->add_arguments(); value_proto->set_argument_index(TagIndex(operation)); value_proto->set_integer_value(value); + value_proto->set_type(CpArgument::INTEGER_VALUE); } void VisitIntervalVariable(const IntervalVar* const variable, @@ -701,10 +713,10 @@ class SecondPassVisitor : public ModelVisitor { IntervalVar* const delegate) override { if (delegate != nullptr) { const int index = model_proto_->intervals_size(); - CPIntervalVariableProto* const var_proto = model_proto_->add_intervals(); + CpIntervalVariable* const var_proto = model_proto_->add_intervals(); var_proto->set_index(index); var_proto->set_type_index(TagIndex(ModelVisitor::kIntervalVariable)); - CPArgumentProto* const sub_proto = var_proto->add_arguments(); + CpArgument* const sub_proto = var_proto->add_arguments(); sub_proto->set_argument_index(TagIndex(operation)); sub_proto->set_interval_index(FindIntervalIndexOrDie(delegate)); sub_proto->set_integer_value(value); @@ -715,37 +727,37 @@ class SecondPassVisitor : public ModelVisitor { } } else { const int index = model_proto_->intervals_size(); - CPIntervalVariableProto* const var_proto = model_proto_->add_intervals(); + CpIntervalVariable* const var_proto = model_proto_->add_intervals(); var_proto->set_index(index); var_proto->set_type_index(TagIndex(ModelVisitor::kIntervalVariable)); if (variable->HasName()) { var_proto->set_name(variable->name()); } - CPArgumentProto* const start_min_proto = var_proto->add_arguments(); + CpArgument* const start_min_proto = var_proto->add_arguments(); start_min_proto->set_argument_index( TagIndex(ModelVisitor::kStartMinArgument)); start_min_proto->set_integer_value(variable->StartMin()); - CPArgumentProto* const start_max_proto = var_proto->add_arguments(); + CpArgument* const start_max_proto = var_proto->add_arguments(); start_max_proto->set_argument_index( TagIndex(ModelVisitor::kStartMaxArgument)); start_max_proto->set_integer_value(variable->StartMax()); - CPArgumentProto* const end_min_proto = var_proto->add_arguments(); + CpArgument* const end_min_proto = var_proto->add_arguments(); end_min_proto->set_argument_index( TagIndex(ModelVisitor::kEndMinArgument)); end_min_proto->set_integer_value(variable->EndMin()); - CPArgumentProto* const end_max_proto = var_proto->add_arguments(); + CpArgument* const end_max_proto = var_proto->add_arguments(); end_max_proto->set_argument_index( TagIndex(ModelVisitor::kEndMaxArgument)); end_max_proto->set_integer_value(variable->EndMax()); - CPArgumentProto* const duration_min_proto = var_proto->add_arguments(); + CpArgument* const duration_min_proto = var_proto->add_arguments(); duration_min_proto->set_argument_index( TagIndex(ModelVisitor::kDurationMinArgument)); duration_min_proto->set_integer_value(variable->DurationMin()); - CPArgumentProto* const duration_max_proto = var_proto->add_arguments(); + CpArgument* const duration_max_proto = var_proto->add_arguments(); duration_max_proto->set_argument_index( TagIndex(ModelVisitor::kDurationMaxArgument)); duration_max_proto->set_integer_value(variable->DurationMax()); - CPArgumentProto* const optional_proto = var_proto->add_arguments(); + CpArgument* const optional_proto = var_proto->add_arguments(); optional_proto->set_argument_index( TagIndex(ModelVisitor::kOptionalArgument)); optional_proto->set_integer_value(!variable->MustBePerformed()); @@ -754,18 +766,19 @@ class SecondPassVisitor : public ModelVisitor { void VisitSequenceVariable(const SequenceVar* const sequence) override { const int index = model_proto_->sequences_size(); - CPSequenceVariableProto* const var_proto = model_proto_->add_sequences(); + CpSequenceVariable* const var_proto = model_proto_->add_sequences(); var_proto->set_index(index); var_proto->set_type_index(TagIndex(ModelVisitor::kSequenceVariable)); if (sequence->HasName()) { var_proto->set_name(sequence->name()); } - CPArgumentProto* const sub_proto = var_proto->add_arguments(); + CpArgument* const sub_proto = var_proto->add_arguments(); sub_proto->set_argument_index(TagIndex(ModelVisitor::kIntervalsArgument)); for (int i = 0; i < sequence->size(); ++i) { IntervalVar* const interval = sequence->Interval(i); sub_proto->add_interval_array(FindIntervalIndexOrDie(interval)); } + sub_proto->set_type(CpArgument::INTERVAL_ARRAY); } int TagIndex(const std::string& tag) { return tags_.Add(tag); } @@ -790,7 +803,7 @@ class SecondPassVisitor : public ModelVisitor { const int64 step = holder->FindIntegerArgumentOrDie(kStepArgument); const int objective_index = holder->FindIntegerExpressionArgumentOrDie(kExpressionArgument); - CPObjectiveProto* const objective_proto = model_proto_->mutable_objective(); + CpObjective* const objective_proto = model_proto_->mutable_objective(); objective_proto->set_maximize(maximize); objective_proto->set_step(step); objective_proto->set_objective_index(objective_index); @@ -798,7 +811,7 @@ class SecondPassVisitor : public ModelVisitor { void WriteSearchLimit(ArgumentHolder* const holder) { CHECK(holder != nullptr); - SearchLimitProto* const proto = model_proto_->mutable_search_limit(); + SearchLimitParameters* const proto = model_proto_->mutable_search_limit(); proto->set_time( holder->FindIntegerArgumentWithDefault(kTimeLimitArgument, kint64max)); proto->set_branches(holder->FindIntegerArgumentWithDefault( @@ -814,7 +827,7 @@ class SecondPassVisitor : public ModelVisitor { } void WriteVariableGroup(ArgumentHolder* const holder) { - CPVariableGroup* const group_proto = model_proto_->add_variable_groups(); + CpVariableGroup* const group_proto = model_proto_->add_variable_groups(); holder->ExportToProto(&tags_, group_proto); } @@ -830,7 +843,7 @@ class SecondPassVisitor : public ModelVisitor { } top()->ExportToProto(&tags_, proto); for (ArgumentHolder* const arg : extensions_) { - CPExtensionProto* const extension_proto = proto->add_extensions(); + CpExtension* const extension_proto = proto->add_extensions(); extension_proto->set_type_index(TagIndex(arg->type_name())); arg->ExportToProto(&tags_, extension_proto); } @@ -882,7 +895,7 @@ class SecondPassVisitor : public ModelVisitor { std::vector constraint_list_; std::vector interval_list_; std::vector sequence_list_; - CPModelProto* const model_proto_; + CpModel* const model_proto_; std::vector holders_; std::vector extensions_; @@ -926,8 +939,8 @@ class ArrayWithOffset : public BaseObject { }; template -std::function MakeFunctionFromProto(CPModelLoader* const builder, - const CPExtensionProto& proto, +std::function MakeFunctionFromProto(CpModelLoader* const builder, + const CpExtension& proto, int tag_index) { DCHECK_EQ(tag_index, proto.type_index()); Solver* const solver = builder->solver(); @@ -954,8 +967,8 @@ std::function MakeFunctionFromProto(CPModelLoader* const builder, // ----- kAbs ----- -IntExpr* BuildAbs(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildAbs(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* expr = nullptr; VERIFY( builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); @@ -964,8 +977,8 @@ IntExpr* BuildAbs(CPModelLoader* const builder, // ----- kAbsEqual ----- -Constraint* BuildAbsEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildAbsEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* expr = nullptr; VERIFY( builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); @@ -976,8 +989,8 @@ Constraint* BuildAbsEqual(CPModelLoader* const builder, // ----- kAllDifferent ----- -Constraint* BuildAllDifferent(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildAllDifferent(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); int64 escape = 0; @@ -992,8 +1005,8 @@ Constraint* BuildAllDifferent(CPModelLoader* const builder, // ----- kAllowedAssignments ----- -Constraint* BuildAllowedAssignments(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildAllowedAssignments(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); IntTupleSet tuples(vars.size()); @@ -1003,8 +1016,8 @@ Constraint* BuildAllowedAssignments(CPModelLoader* const builder, // ----- kBetween ----- -Constraint* BuildBetween(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildBetween(CpModelLoader* const builder, + const CpConstraint& proto) { int64 value_min = 0; VERIFY(builder->ScanArguments(ModelVisitor::kMinArgument, proto, &value_min)); int64 value_max = 0; @@ -1017,8 +1030,8 @@ Constraint* BuildBetween(CPModelLoader* const builder, // ----- kConditionalExpr ----- -IntExpr* BuildConditionalExpr(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildConditionalExpr(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* condition = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kVariableArgument, proto, &condition)); @@ -1033,8 +1046,8 @@ IntExpr* BuildConditionalExpr(CPModelLoader* const builder, // ----- kCircuit ----- -Constraint* BuildCircuit(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildCircuit(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kNextsArgument, proto, &vars)); int64 v; @@ -1048,8 +1061,8 @@ Constraint* BuildCircuit(CPModelLoader* const builder, // ----- kConvexPiecewise ----- -IntExpr* BuildConvexPiecewise(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildConvexPiecewise(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* expr = nullptr; VERIFY( builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); @@ -1071,8 +1084,8 @@ IntExpr* BuildConvexPiecewise(CPModelLoader* const builder, // ----- kCountEqual ----- -Constraint* BuildCountEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildCountEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); int64 value = 0; @@ -1090,8 +1103,8 @@ Constraint* BuildCountEqual(CPModelLoader* const builder, // ----- kCover ----- -Constraint* BuildCover(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildCover(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY( builder->ScanArguments(ModelVisitor::kIntervalsArgument, proto, &vars)); @@ -1102,8 +1115,8 @@ Constraint* BuildCover(CPModelLoader* const builder, // ----- kCumulative ----- -Constraint* BuildCumulative(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildCumulative(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY( builder->ScanArguments(ModelVisitor::kIntervalsArgument, proto, &vars)); @@ -1114,7 +1127,7 @@ Constraint* BuildCumulative(CPModelLoader* const builder, VERIFY(builder->ScanArguments(ModelVisitor::kCapacityArgument, proto, &capacity)); std::string name; - if (proto.has_name()) { + if (!proto.name().empty()) { name = proto.name(); } return builder->solver()->MakeCumulative(vars, demands, capacity, name); @@ -1122,8 +1135,8 @@ Constraint* BuildCumulative(CPModelLoader* const builder, // ----- kDeviation ----- -Constraint* BuildDeviation(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildDeviation(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); IntExpr* target = nullptr; @@ -1135,8 +1148,8 @@ Constraint* BuildDeviation(CPModelLoader* const builder, // ----- kDifference ----- -IntExpr* BuildDifference(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildDifference(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1153,8 +1166,8 @@ IntExpr* BuildDifference(CPModelLoader* const builder, // ----- kDisjunctive ----- -Constraint* BuildDisjunctive(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildDisjunctive(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; if (builder->ScanArguments(ModelVisitor::kIntervalsArgument, proto, &vars)) { return builder->solver()->MakeDisjunctiveConstraint(vars, proto.name()); @@ -1173,8 +1186,8 @@ Constraint* BuildDisjunctive(CPModelLoader* const builder, // ----- kDistribute ----- -Constraint* BuildDistribute(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildDistribute(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; if (builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)) { std::vector cards; @@ -1208,8 +1221,8 @@ Constraint* BuildDistribute(CPModelLoader* const builder, // ----- kDivide ----- -IntExpr* BuildDivide(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildDivide(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1226,8 +1239,8 @@ IntExpr* BuildDivide(CPModelLoader* const builder, // ----- kDurationExpr ----- -IntExpr* BuildDurationExpr(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildDurationExpr(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntervalVar* var = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); return var->DurationExpr(); @@ -1235,8 +1248,8 @@ IntExpr* BuildDurationExpr(CPModelLoader* const builder, // ----- kElement ----- -IntExpr* BuildElement(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildElement(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* index = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index)); std::vector values; @@ -1260,8 +1273,8 @@ IntExpr* BuildElement(CPModelLoader* const builder, // ----- kElementEqual ----- -Constraint* BuildElementEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildElementEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* index = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index)); std::vector values; @@ -1293,8 +1306,8 @@ Constraint* BuildElementEqual(CPModelLoader* const builder, // ----- kEndExpr ----- -IntExpr* BuildEndExpr(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildEndExpr(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntervalVar* var = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); return var->EndExpr(); @@ -1302,8 +1315,8 @@ IntExpr* BuildEndExpr(CPModelLoader* const builder, // ----- kEquality ----- -Constraint* BuildEquality(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildEquality(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1328,15 +1341,15 @@ Constraint* BuildEquality(CPModelLoader* const builder, // ----- kFalseConstraint ----- -Constraint* BuildFalseConstraint(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildFalseConstraint(CpModelLoader* const builder, + const CpConstraint& proto) { return builder->solver()->MakeFalseConstraint(); } // ----- kGreater ----- -Constraint* BuildGreater(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildGreater(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* left = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); IntExpr* right = nullptr; @@ -1346,8 +1359,8 @@ Constraint* BuildGreater(CPModelLoader* const builder, // ----- kGreaterOrEqual ----- -Constraint* BuildGreaterOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildGreaterOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1365,8 +1378,8 @@ Constraint* BuildGreaterOrEqual(CPModelLoader* const builder, // ----- kIndexOf ----- -Constraint* BuildIndexOf(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIndexOf(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* index = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index)); std::vector vars; @@ -1380,13 +1393,13 @@ Constraint* BuildIndexOf(CPModelLoader* const builder, // ----- kIntegerVariable ----- -IntExpr* BuildIntegerVariable(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildIntegerVariable(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* sub_expression = nullptr; if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &sub_expression)) { IntVar* const result = sub_expression->Var(); - if (proto.has_name()) { + if (!proto.name().empty()) { result->set_name(proto.name()); } return result; @@ -1405,7 +1418,7 @@ IntExpr* BuildIntegerVariable(CPModelLoader* const builder, &value)) { result = builder->solver()->MakeProd(sub_var, value); } - if (proto.has_name()) { + if (!proto.name().empty()) { result->set_name(proto.name()); } return result; @@ -1415,7 +1428,7 @@ IntExpr* BuildIntegerVariable(CPModelLoader* const builder, int64 var_max = 0; VERIFY(builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &var_max)); IntVar* const result = builder->solver()->MakeIntVar(var_min, var_max); - if (proto.has_name()) { + if (!proto.name().empty()) { result->set_name(proto.name()); } return result; @@ -1423,7 +1436,7 @@ IntExpr* BuildIntegerVariable(CPModelLoader* const builder, std::vector values; if (builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)) { IntVar* const result = builder->solver()->MakeIntVar(values); - if (proto.has_name()) { + if (!proto.name().empty()) { result->set_name(proto.name()); } return result; @@ -1433,8 +1446,8 @@ IntExpr* BuildIntegerVariable(CPModelLoader* const builder, // ----- kIntervalBinaryRelation ----- -Constraint* BuildIntervalBinaryRelation(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIntervalBinaryRelation(CpModelLoader* const builder, + const CpConstraint& proto) { IntervalVar* left = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); IntervalVar* right = nullptr; @@ -1449,8 +1462,8 @@ Constraint* BuildIntervalBinaryRelation(CPModelLoader* const builder, // ----- kIntervalDisjunction ----- -Constraint* BuildIntervalDisjunction(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIntervalDisjunction(CpModelLoader* const builder, + const CpConstraint& proto) { IntervalVar* left = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); IntervalVar* right = nullptr; @@ -1462,8 +1475,8 @@ Constraint* BuildIntervalDisjunction(CPModelLoader* const builder, // ----- kIntervalUnaryRelation ----- -Constraint* BuildIntervalUnaryRelation(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIntervalUnaryRelation(CpModelLoader* const builder, + const CpConstraint& proto) { IntervalVar* interval = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &interval)); @@ -1479,8 +1492,8 @@ Constraint* BuildIntervalUnaryRelation(CPModelLoader* const builder, // ----- kIntervalVariable ----- -IntervalVar* BuildIntervalVariable(CPModelLoader* const builder, - const CPIntervalVariableProto& proto) { +IntervalVar* BuildIntervalVariable(CpModelLoader* const builder, + const CpIntervalVariable& proto) { Solver* const solver = builder->solver(); int64 start_min = 0; if (builder->ScanArguments(ModelVisitor::kStartMinArgument, proto, @@ -1515,7 +1528,7 @@ IntervalVar* BuildIntervalVariable(CPModelLoader* const builder, } } else { VERIFY_EQ(1, proto.arguments_size()); - const CPArgumentProto& sub_proto = proto.arguments(0); + const CpArgument& sub_proto = proto.arguments(0); IntervalVar* const derived = builder->IntervalVariable(sub_proto.interval_index()); const int operation_index = sub_proto.argument_index(); @@ -1549,8 +1562,8 @@ IntervalVar* BuildIntervalVariable(CPModelLoader* const builder, // ----- kInversePermutation ----- -Constraint* BuildInversePermutation(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildInversePermutation(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector left; VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); std::vector right; @@ -1560,8 +1573,8 @@ Constraint* BuildInversePermutation(CPModelLoader* const builder, // ----- kIsBetween ----- -Constraint* BuildIsBetween(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsBetween(CpModelLoader* const builder, + const CpConstraint& proto) { int64 value_min = 0; VERIFY(builder->ScanArguments(ModelVisitor::kMinArgument, proto, &value_min)); int64 value_max = 0; @@ -1577,8 +1590,8 @@ Constraint* BuildIsBetween(CPModelLoader* const builder, // ----- kIsDifferent ----- -Constraint* BuildIsDifferent(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsDifferent(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* target = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); IntExpr* left = nullptr; @@ -1598,8 +1611,8 @@ Constraint* BuildIsDifferent(CPModelLoader* const builder, // ----- kIsEqual ----- -Constraint* BuildIsEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* target = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); IntExpr* left = nullptr; @@ -1619,8 +1632,8 @@ Constraint* BuildIsEqual(CPModelLoader* const builder, // ----- kIsGreater ----- -Constraint* BuildIsGreater(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsGreater(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* target = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); IntExpr* left = nullptr; @@ -1632,8 +1645,8 @@ Constraint* BuildIsGreater(CPModelLoader* const builder, // ----- kIsGreaterOrEqual ----- -Constraint* BuildIsGreaterOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsGreaterOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* target = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); IntExpr* left = nullptr; @@ -1655,8 +1668,8 @@ Constraint* BuildIsGreaterOrEqual(CPModelLoader* const builder, // ----- kIsLess ----- -Constraint* BuildIsLess(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsLess(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* target = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); IntExpr* left = nullptr; @@ -1668,8 +1681,8 @@ Constraint* BuildIsLess(CPModelLoader* const builder, // ----- kIsLessOrEqual ----- -Constraint* BuildIsLessOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsLessOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* target = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); IntExpr* left = nullptr; @@ -1690,8 +1703,8 @@ Constraint* BuildIsLessOrEqual(CPModelLoader* const builder, // ----- kIsMember ----- -Constraint* BuildIsMember(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildIsMember(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector values; VERIFY(builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)); IntExpr* expr = nullptr; @@ -1704,8 +1717,7 @@ Constraint* BuildIsMember(CPModelLoader* const builder, // ----- kLess ----- -Constraint* BuildLess(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildLess(CpModelLoader* const builder, const CpConstraint& proto) { IntExpr* left = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); IntExpr* right = nullptr; @@ -1715,8 +1727,8 @@ Constraint* BuildLess(CPModelLoader* const builder, // ----- kLessOrEqual ----- -Constraint* BuildLessOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildLessOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1734,8 +1746,8 @@ Constraint* BuildLessOrEqual(CPModelLoader* const builder, // ----- kLexLess ----- -Constraint* BuildLexLess(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildLexLess(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector left; VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); std::vector right; @@ -1748,8 +1760,8 @@ Constraint* BuildLexLess(CPModelLoader* const builder, // ----- kMapDomain ----- -Constraint* BuildMapDomain(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildMapDomain(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); IntExpr* target = nullptr; @@ -1759,8 +1771,8 @@ Constraint* BuildMapDomain(CPModelLoader* const builder, // ----- kMax ----- -IntExpr* BuildMax(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildMax(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1780,8 +1792,8 @@ IntExpr* BuildMax(CPModelLoader* const builder, // ----- kMaxEqual ----- -Constraint* BuildMaxEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildMaxEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); IntExpr* target = nullptr; @@ -1791,8 +1803,8 @@ Constraint* BuildMaxEqual(CPModelLoader* const builder, // ----- kMember ----- -Constraint* BuildMember(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildMember(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector values; VERIFY(builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)); IntExpr* expr = nullptr; @@ -1803,8 +1815,8 @@ Constraint* BuildMember(CPModelLoader* const builder, // ----- kMin ----- -IntExpr* BuildMin(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildMin(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1824,8 +1836,8 @@ IntExpr* BuildMin(CPModelLoader* const builder, // ----- kMinEqual ----- -Constraint* BuildMinEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildMinEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); IntExpr* target = nullptr; @@ -1835,8 +1847,8 @@ Constraint* BuildMinEqual(CPModelLoader* const builder, // ----- kNoCycle ----- -Constraint* BuildNoCycle(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildNoCycle(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector nexts; VERIFY(builder->ScanArguments(ModelVisitor::kNextsArgument, proto, &nexts)); std::vector active; @@ -1858,8 +1870,8 @@ Constraint* BuildNoCycle(CPModelLoader* const builder, // ----- kNonEqual ----- -Constraint* BuildNonEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildNonEqual(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -1877,8 +1889,8 @@ Constraint* BuildNonEqual(CPModelLoader* const builder, // ----- kNullIntersect ----- -Constraint* BuildNullIntersect(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildNullIntersect(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector left; VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); std::vector right; @@ -1893,8 +1905,8 @@ Constraint* BuildNullIntersect(CPModelLoader* const builder, // ----- kOpposite ----- -IntExpr* BuildOpposite(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildOpposite(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* expr = nullptr; VERIFY( builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); @@ -1904,8 +1916,8 @@ IntExpr* BuildOpposite(CPModelLoader* const builder, // ----- kPack ----- bool AddUsageLessConstantDimension(Pack* const pack, - CPModelLoader* const builder, - const CPExtensionProto& proto) { + CpModelLoader* const builder, + const CpExtension& proto) { std::vector weights; VERIFY_BOOL(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, &weights)); @@ -1917,8 +1929,8 @@ bool AddUsageLessConstantDimension(Pack* const pack, } bool AddCountAssignedItemsDimension(Pack* const pack, - CPModelLoader* const builder, - const CPExtensionProto& proto) { + CpModelLoader* const builder, + const CpExtension& proto) { IntExpr* target = nullptr; VERIFY_BOOL( builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); @@ -1926,8 +1938,8 @@ bool AddCountAssignedItemsDimension(Pack* const pack, return true; } -bool AddCountUsedBinDimension(Pack* const pack, CPModelLoader* const builder, - const CPExtensionProto& proto) { +bool AddCountUsedBinDimension(Pack* const pack, CpModelLoader* const builder, + const CpExtension& proto) { IntExpr* target = nullptr; VERIFY_BOOL( builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); @@ -1936,8 +1948,8 @@ bool AddCountUsedBinDimension(Pack* const pack, CPModelLoader* const builder, } bool AddUsageEqualVariableDimension(Pack* const pack, - CPModelLoader* const builder, - const CPExtensionProto& proto) { + CpModelLoader* const builder, + const CpExtension& proto) { std::vector weights; VERIFY_BOOL(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, &weights)); @@ -1949,8 +1961,8 @@ bool AddUsageEqualVariableDimension(Pack* const pack, } bool AddVariableUsageLessConstantDimension(Pack* const pack, - CPModelLoader* const builder, - const CPExtensionProto& proto) { + CpModelLoader* const builder, + const CpExtension& proto) { std::vector uppers; VERIFY_BOOL( builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &uppers)); @@ -1962,8 +1974,8 @@ bool AddVariableUsageLessConstantDimension(Pack* const pack, } bool AddWeightedSumOfAssignedDimension(Pack* const pack, - CPModelLoader* const builder, - const CPExtensionProto& proto) { + CpModelLoader* const builder, + const CpExtension& proto) { std::vector weights; VERIFY_BOOL(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, &weights)); @@ -1977,8 +1989,7 @@ bool AddWeightedSumOfAssignedDimension(Pack* const pack, #define IS_TYPE(index, builder, tag) \ index == builder->TagIndex(ModelVisitor::tag) -Constraint* BuildPack(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildPack(CpModelLoader* const builder, const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); int64 bins = 0; @@ -1986,7 +1997,7 @@ Constraint* BuildPack(CPModelLoader* const builder, Pack* const pack = builder->solver()->MakePack(vars, bins); // Add dimensions. They are stored as extensions in the proto. for (int i = 0; i < proto.extensions_size(); ++i) { - const CPExtensionProto& dimension_proto = proto.extensions(i); + const CpExtension& dimension_proto = proto.extensions(i); const int type_index = dimension_proto.type_index(); if (IS_TYPE(type_index, builder, kUsageLessConstantExtension)) { VERIFY(AddUsageLessConstantDimension(pack, builder, dimension_proto)); @@ -2014,8 +2025,8 @@ Constraint* BuildPack(CPModelLoader* const builder, // ----- kPathCumul ----- -Constraint* BuildPathCumul(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildPathCumul(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector nexts; VERIFY(builder->ScanArguments(ModelVisitor::kNextsArgument, proto, &nexts)); std::vector active; @@ -2030,8 +2041,8 @@ Constraint* BuildPathCumul(CPModelLoader* const builder, // ----- kPerformedExpr ----- -IntExpr* BuildPerformedExpr(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildPerformedExpr(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntervalVar* var = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); return var->PerformedExpr(); @@ -2039,8 +2050,8 @@ IntExpr* BuildPerformedExpr(CPModelLoader* const builder, // ----- kPower ----- -IntExpr* BuildPower(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildPower(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* expr = nullptr; VERIFY( builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); @@ -2051,8 +2062,8 @@ IntExpr* BuildPower(CPModelLoader* const builder, // ----- kProduct ----- -IntExpr* BuildProduct(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildProduct(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -2069,8 +2080,8 @@ IntExpr* BuildProduct(CPModelLoader* const builder, // ----- kScalProd ----- -IntExpr* BuildScalProd(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildScalProd(CpModelLoader* const builder, + const CpIntegerExpression& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); std::vector values; @@ -2081,8 +2092,8 @@ IntExpr* BuildScalProd(CPModelLoader* const builder, // ----- kScalProdEqual ----- -Constraint* BuildScalProdEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildScalProdEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); std::vector values; @@ -2095,8 +2106,8 @@ Constraint* BuildScalProdEqual(CPModelLoader* const builder, // ----- kScalProdGreaterOrEqual ----- -Constraint* BuildScalProdGreaterOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildScalProdGreaterOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); std::vector values; @@ -2109,8 +2120,8 @@ Constraint* BuildScalProdGreaterOrEqual(CPModelLoader* const builder, // ----- kScalProdLessOrEqual ----- -Constraint* BuildScalProdLessOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildScalProdLessOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); std::vector values; @@ -2123,8 +2134,8 @@ Constraint* BuildScalProdLessOrEqual(CPModelLoader* const builder, // ----- kSemiContinuous ----- -IntExpr* BuildSemiContinuous(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildSemiContinuous(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* expr = nullptr; VERIFY( builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); @@ -2138,8 +2149,8 @@ IntExpr* BuildSemiContinuous(CPModelLoader* const builder, // ----- kSortingConstraint ----- -Constraint* BuildSortingConstraint(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildSortingConstraint(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); std::vector targets; @@ -2150,8 +2161,8 @@ Constraint* BuildSortingConstraint(CPModelLoader* const builder, // ----- kSquare ----- -IntExpr* BuildSquare(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildSquare(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* expr = nullptr; VERIFY( builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); @@ -2160,8 +2171,8 @@ IntExpr* BuildSquare(CPModelLoader* const builder, // ----- kStartExpr ----- -IntExpr* BuildStartExpr(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildStartExpr(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntervalVar* var = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); return var->StartExpr(); @@ -2169,8 +2180,8 @@ IntExpr* BuildStartExpr(CPModelLoader* const builder, // ----- kSum ----- -IntExpr* BuildSum(CPModelLoader* const builder, - const CPIntegerExpressionProto& proto) { +IntExpr* BuildSum(CpModelLoader* const builder, + const CpIntegerExpression& proto) { IntExpr* left = nullptr; if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { IntExpr* right = nullptr; @@ -2190,8 +2201,8 @@ IntExpr* BuildSum(CPModelLoader* const builder, // ----- kSumEqual ----- -Constraint* BuildSumEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildSumEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); int64 value = 0; @@ -2205,8 +2216,8 @@ Constraint* BuildSumEqual(CPModelLoader* const builder, // ----- kSumGreaterOrEqual ----- -Constraint* BuildSumGreaterOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildSumGreaterOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); int64 value = 0; @@ -2216,8 +2227,8 @@ Constraint* BuildSumGreaterOrEqual(CPModelLoader* const builder, // ----- kSumLessOrEqual ----- -Constraint* BuildSumLessOrEqual(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildSumLessOrEqual(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); int64 value = 0; @@ -2227,8 +2238,8 @@ Constraint* BuildSumLessOrEqual(CPModelLoader* const builder, // ----- kTransition ----- -Constraint* BuildTransition(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildTransition(CpModelLoader* const builder, + const CpConstraint& proto) { std::vector vars; VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); IntTupleSet tuples(3); @@ -2246,15 +2257,15 @@ Constraint* BuildTransition(CPModelLoader* const builder, // ----- kTrueConstraint ----- -Constraint* BuildTrueConstraint(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildTrueConstraint(CpModelLoader* const builder, + const CpConstraint& proto) { return builder->solver()->MakeTrueConstraint(); } // ----- kVarValueWatcher ----- -Constraint* BuildVarValueWatcher(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildVarValueWatcher(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* expr = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kVariableArgument, proto, &expr)); std::vector vars; @@ -2266,8 +2277,8 @@ Constraint* BuildVarValueWatcher(CPModelLoader* const builder, // ----- kVarBoundWatcher ----- -Constraint* BuildVarBoundWatcher(CPModelLoader* const builder, - const CPConstraintProto& proto) { +Constraint* BuildVarBoundWatcher(CpModelLoader* const builder, + const CpConstraint& proto) { IntExpr* expr = nullptr; VERIFY(builder->ScanArguments(ModelVisitor::kVariableArgument, proto, &expr)); std::vector vars; @@ -2281,9 +2292,9 @@ Constraint* BuildVarBoundWatcher(CPModelLoader* const builder, #undef VERIFY_EQ } // namespace -// ----- CPModelLoader ----- +// ----- CpModelLoader ----- -bool CPModelLoader::BuildFromProto(const CPIntegerExpressionProto& proto) { +bool CpModelLoader::BuildFromProto(const CpIntegerExpression& proto) { const int index = proto.index(); const int tag_index = proto.type_index(); Solver::IntegerExpressionBuilder builder = @@ -2302,7 +2313,7 @@ bool CPModelLoader::BuildFromProto(const CPIntegerExpressionProto& proto) { return true; } -Constraint* CPModelLoader::BuildFromProto(const CPConstraintProto& proto) { +Constraint* CpModelLoader::BuildFromProto(const CpConstraint& proto) { const int tag_index = proto.type_index(); Solver::ConstraintBuilder builder = solver_->GetConstraintBuilder(tags_.Element(tag_index)); @@ -2314,7 +2325,7 @@ Constraint* CPModelLoader::BuildFromProto(const CPConstraintProto& proto) { return built; } -bool CPModelLoader::BuildFromProto(const CPIntervalVariableProto& proto) { +bool CpModelLoader::BuildFromProto(const CpIntervalVariable& proto) { const int index = proto.index(); const int tag_index = proto.type_index(); Solver::IntervalVariableBuilder builder = @@ -2332,7 +2343,7 @@ bool CPModelLoader::BuildFromProto(const CPIntervalVariableProto& proto) { return true; } -bool CPModelLoader::BuildFromProto(const CPSequenceVariableProto& proto) { +bool CpModelLoader::BuildFromProto(const CpSequenceVariable& proto) { const int index = proto.index(); const int tag_index = proto.type_index(); Solver::SequenceVariableBuilder builder = @@ -2350,36 +2361,34 @@ bool CPModelLoader::BuildFromProto(const CPSequenceVariableProto& proto) { return true; } -IntExpr* CPModelLoader::IntegerExpression(int index) const { +IntExpr* CpModelLoader::IntegerExpression(int index) const { CHECK_GE(index, 0); CHECK_LT(index, expressions_.size()); CHECK(expressions_[index] != nullptr); return expressions_[index]; } -IntervalVar* CPModelLoader::IntervalVariable(int index) const { +IntervalVar* CpModelLoader::IntervalVariable(int index) const { CHECK_GE(index, 0); CHECK_LT(index, intervals_.size()); CHECK(intervals_[index] != nullptr); return intervals_[index]; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, int64* to_fill) { if (arg_proto.argument_index() == type_index && - arg_proto.has_integer_value()) { + arg_proto.type() == CpArgument::INTEGER_VALUE) { *to_fill = arg_proto.integer_value(); return true; } return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, IntExpr** to_fill) { if (arg_proto.argument_index() == type_index && - arg_proto.has_integer_expression_index()) { + arg_proto.type() == CpArgument::EXPRESSION) { const int expression_index = arg_proto.integer_expression_index(); CHECK(expressions_[expression_index] != nullptr); *to_fill = expressions_[expression_index]; @@ -2388,10 +2397,10 @@ bool CPModelLoader::ScanOneArgument(int type_index, return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill) { - if (arg_proto.argument_index() == type_index) { + if (arg_proto.argument_index() == type_index && + arg_proto.type() == CpArgument::INTEGER_ARRAY) { const int values_size = arg_proto.integer_array_size(); for (int j = 0; j < values_size; ++j) { to_fill->push_back(arg_proto.integer_array(j)); @@ -2401,13 +2410,12 @@ bool CPModelLoader::ScanOneArgument(int type_index, return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, IntTupleSet* to_fill) { if (arg_proto.argument_index() == type_index && arg_proto.has_integer_matrix()) { to_fill->Clear(); - const CPIntegerMatrixProto& matrix = arg_proto.integer_matrix(); + const CpIntegerMatrix& matrix = arg_proto.integer_matrix(); const int rows = matrix.rows(); const int columns = matrix.columns(); int counter = 0; @@ -2425,10 +2433,10 @@ bool CPModelLoader::ScanOneArgument(int type_index, return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill) { - if (arg_proto.argument_index() == type_index) { + if (arg_proto.argument_index() == type_index && + arg_proto.type() == CpArgument::EXPRESSION_ARRAY) { const int vars_size = arg_proto.integer_expression_array_size(); for (int j = 0; j < vars_size; ++j) { const int expression_index = arg_proto.integer_expression_array(j); @@ -2440,11 +2448,10 @@ bool CPModelLoader::ScanOneArgument(int type_index, return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, IntervalVar** to_fill) { if (arg_proto.argument_index() == type_index && - arg_proto.has_interval_index()) { + arg_proto.type() == CpArgument::INTERVAL) { const int interval_index = arg_proto.interval_index(); CHECK(intervals_[interval_index] != nullptr); *to_fill = intervals_[interval_index]; @@ -2453,10 +2460,10 @@ bool CPModelLoader::ScanOneArgument(int type_index, return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill) { - if (arg_proto.argument_index() == type_index) { + if (arg_proto.argument_index() == type_index && + arg_proto.type() == CpArgument::INTERVAL_ARRAY) { const int vars_size = arg_proto.interval_array_size(); for (int j = 0; j < vars_size; ++j) { const int interval_index = arg_proto.interval_array(j); @@ -2468,11 +2475,10 @@ bool CPModelLoader::ScanOneArgument(int type_index, return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, SequenceVar** to_fill) { if (arg_proto.argument_index() == type_index && - arg_proto.has_sequence_index()) { + arg_proto.type() == CpArgument::SEQUENCE) { const int sequence_index = arg_proto.sequence_index(); CHECK(sequences_[sequence_index] != nullptr); *to_fill = sequences_[sequence_index]; @@ -2481,10 +2487,10 @@ bool CPModelLoader::ScanOneArgument(int type_index, return false; } -bool CPModelLoader::ScanOneArgument(int type_index, - const CPArgumentProto& arg_proto, +bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, std::vector* to_fill) { - if (arg_proto.argument_index() == type_index) { + if (arg_proto.argument_index() == type_index && + arg_proto.type() == CpArgument::SEQUENCE_ARRAY) { const int vars_size = arg_proto.sequence_array_size(); for (int j = 0; j < vars_size; ++j) { const int sequence_index = arg_proto.sequence_array(j); @@ -2499,7 +2505,7 @@ bool CPModelLoader::ScanOneArgument(int type_index, // ----- Solver API ----- void Solver::ExportModel(const std::vector& monitors, - CPModelProto* const model_proto, + CpModel* const model_proto, DecisionBuilder* const db) const { CHECK(model_proto != nullptr); FirstPassVisitor first_pass; @@ -2509,7 +2515,7 @@ void Solver::ExportModel(const std::vector& monitors, } void Solver::ExportModel(const std::vector& monitors, - CPModelProto* const model_proto) const { + CpModel* const model_proto) const { CHECK(model_proto != nullptr); FirstPassVisitor first_pass; Accept(&first_pass, monitors); @@ -2517,7 +2523,7 @@ void Solver::ExportModel(const std::vector& monitors, Accept(&second_pass, monitors); } -void Solver::ExportModel(CPModelProto* const model_proto) const { +void Solver::ExportModel(CpModel* const model_proto) const { CHECK(model_proto != nullptr); FirstPassVisitor first_pass; Accept(&first_pass); @@ -2525,11 +2531,11 @@ void Solver::ExportModel(CPModelProto* const model_proto) const { Accept(&second_pass); } -bool Solver::LoadModel(const CPModelProto& model_proto) { +bool Solver::LoadModel(const CpModel& model_proto) { return LoadModel(model_proto, nullptr); } -bool Solver::LoadModel(const CPModelProto& model_proto, +bool Solver::LoadModel(const CpModel& model_proto, std::vector* monitors) { if (model_proto.version() > kModelVersion) { LOG(ERROR) << "Model protocol buffer version is greater than" @@ -2537,7 +2543,7 @@ bool Solver::LoadModel(const CPModelProto& model_proto, << " vs " << kModelVersion << ")"; return false; } - CPModelLoader builder(this); + CpModelLoader builder(this); for (int i = 0; i < model_proto.tags_size(); ++i) { builder.AddTag(model_proto.tags(i)); } @@ -2581,7 +2587,7 @@ bool Solver::LoadModel(const CPModelProto& model_proto, monitors->push_back(MakeLimit(model_proto.search_limit())); } if (model_proto.has_objective()) { - const CPObjectiveProto& objective_proto = model_proto.objective(); + const CpObjective& objective_proto = model_proto.objective(); IntVar* const objective_var = builder.IntegerExpression(objective_proto.objective_index())->Var(); const bool maximize = objective_proto.maximize(); @@ -2594,7 +2600,7 @@ bool Solver::LoadModel(const CPModelProto& model_proto, return true; } -bool Solver::UpgradeModel(CPModelProto* const proto) { +bool Solver::UpgradeModel(CpModel* const proto) { if (proto->version() == kModelVersion) { LOG(INFO) << "Model already up to date with version " << kModelVersion; } diff --git a/src/constraint_solver/java/constraint_solver.swig b/src/constraint_solver/java/constraint_solver.swig index 5a9de97290..fe32ea0a94 100644 --- a/src/constraint_solver/java/constraint_solver.swig +++ b/src/constraint_solver/java/constraint_solver.swig @@ -18,19 +18,31 @@ %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" +%include "util/java/proto.swig" // Remove swig warnings %warnfilter(473) operations_research::DecisionBuilder; // TODO(user): Remove this warnfilter. +// We need to forward-declare the proto here, so that PROTO_INPUT involving it +// works correctly. The order matters very much: this declaration needs to be +// before the %{ #include ".../constraint_solver.h" %}. +namespace operations_research { +class ConstraintSolverParameters; +class SearchLimitParameters; +} // namespace operations_research + + +// Include the files we want to wrap a first time. %{ #include "constraint_solver/constraint_solver.h" #include "constraint_solver/constraint_solveri.h" #include "constraint_solver/java/javawrapcp_util.h" +#include "constraint_solver/search_limit.pb.h" +#include "constraint_solver/solver_parameters.pb.h" // Supporting structure for the PROTECT_FROM_FAILURE macro. #include "setjmp.h" @@ -94,6 +106,11 @@ PROTECT_FROM_FAILURE(Solver::Fail(), arg1); %module(directors="1", allprotected="1") operations_research; +%typemap(javaimports) operations_research::Solver %{ +import com.google.ortools.constraintsolver.ConstraintSolverParameters; +import com.google.ortools.constraintsolver.SearchLimitParameters; +%} + %feature("director") operations_research::DecisionBuilder; %feature("director") operations_research::Decision; %feature("director") operations_research::SearchMonitor; @@ -122,9 +139,6 @@ class SolverToVoid { // Renaming -// Generic rule (to remove) -%rename("%(lowercamelcase)s", %$isfunction) ""; - %ignore operations_research::Solver::MakeIntVarArray; %ignore operations_research::Solver::MakeBoolVarArray; %ignore operations_research::Solver::MakeFixedDurationIntervalVarArray; @@ -254,6 +268,7 @@ class SolverToVoid { %rename (checkAssignment) operations_research::Solver::CheckAssignment; %rename (compose) operations_research::Solver::Compose; %rename (concatenateOperators) operations_research::Solver::ConcatenateOperators; +%rename (defaultSolverParameters) operations_research::Solver::DefaultSolverParameters; %rename (endSearch) operations_research::Solver::EndSearch; %rename (exportProfilingOverview) operations_research::Solver::ExportProfilingOverview; %rename (fail) operations_research::Solver::Fail; @@ -278,6 +293,7 @@ class SolverToVoid { %rename (makeCustomLimit) operations_research::Solver::MakeCustomLimit; %rename (makeDecisionBuilderFromAssignment) operations_research::Solver::MakeDecisionBuilderFromAssignment; %rename (makeDefaultPhase) operations_research::Solver::MakeDefaultPhase; +%rename (makeDefaultSearchLimitParameters) operations_research::Solver::MakeDefaultSearchLimitParameters; %rename (makeDifference) operations_research::Solver::MakeDifference; %rename (makeDeviation) operations_research::Solver::MakeDeviation; %rename (makeDisjunctiveConstraint) operations_research::Solver::MakeDisjunctiveConstraint; @@ -411,6 +427,7 @@ class SolverToVoid { %rename (removeValue) operations_research::IntVar::RemoveValue; %rename (removeValues) operations_research::IntVar::RemoveValues; %rename (removeInterval) operations_research::IntVar::RemoveInterval; +%rename (contains) operations_research::IntVar::Contains; // Rename rules on Constraint. %rename (var) operations_research::Constraint::Var; @@ -726,18 +743,23 @@ WRAP_OTHER_FUNCTIONS_JAVA("com/google/ortools/constraintsolver/") WRAP_CLASS_TO_VOID_JAVA("com/google/ortools/constraintsolver/", SolverToVoid, Solver) -namespace operations_research { -class LocalSearchPhaseParameters { - public: - LocalSearchPhaseParameters(); - ~LocalSearchPhaseParameters(); -}; -} // namespace operations_research +// Protobuf support +PROTO_INPUT(operations_research::ConstraintSolverParameters, + com.google.ortools.constraintsolver.ConstraintSolverParameters, + parameters) +PROTO2_RETURN(operations_research::ConstraintSolverParameters, + com.google.ortools.constraintsolver.ConstraintSolverParameters) +PROTO_INPUT(operations_research::SearchLimitParameters, + com.google.ortools.constraintsolver.SearchLimitParameters, + proto) +PROTO2_RETURN(operations_research::SearchLimitParameters, + com.google.ortools.constraintsolver.SearchLimitParameters) // Wrap cp includes %include "constraint_solver/constraint_solver.h" %include "constraint_solver/constraint_solveri.h" +%include "constraint_solver/java/javawrapcp_util.h" namespace operations_research { namespace swig_util { diff --git a/src/constraint_solver/java/routing.swig b/src/constraint_solver/java/routing.swig index bf2e973997..2839e06df3 100644 --- a/src/constraint_solver/java/routing.swig +++ b/src/constraint_solver/java/routing.swig @@ -15,6 +15,14 @@ %include "constraint_solver/java/constraint_solver.swig" +// We need to forward-declare the proto here, so that PROTO_INPUT involving it +// works correctly. The order matters very much: this declaration needs to be +// before the %{ #include ".../routing.h" %}. +namespace operations_research { +class RoutingModelParameters; +class RoutingSearchParameters; +} // namespace operations_research + // Include the file we want to wrap a first time. %{ #include "constraint_solver/routing.h" @@ -249,6 +257,8 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 // RoutingModel methods. %rename (solve) Solve; +%rename (solveWithParameters) SolveWithParameters; +%rename (solveFromAssignmentWithParameters) SolveFromAssignmentWithParameters; %rename (setArcCostEvaluatorOfAllVehicles) SetArcCostEvaluatorOfAllVehicles; %rename (setArcCostEvaluatorOfVehicle) SetArcCostEvaluatorOfVehicle; %rename (addDimension) AddDimension; @@ -273,6 +283,7 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 %rename (nexts) Nexts; %rename (nextVar) NextVar; %rename (vehicleVar) VehicleVar; +%rename (vehicleVars) VehicleVars; %rename (activeVar) ActiveVar; %rename (addToAssignment) AddToAssignment; %rename (isVehicleUsed) IsVehicleUsed; @@ -280,15 +291,12 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 %rename (compactAssignment) CompactAssignment; %rename (size) Size; %rename (costVar) CostVar; -%rename (updateTimeLimit) UpdateTimeLimit; %rename (preAssignment) PreAssignment; -%rename (getFirstSolutionStrategy) first_solution_strategy; -%rename (setFirstSolutionStrategy) set_first_solution_strategy; %rename (setFirstSolutionEvaluator) SetFirstSolutionEvaluator; -%rename (getMetaheuristic) metaheuristic; -%rename (setMetaheuristic) set_metaheuristic; %rename (routesToAssignment) RoutesToAssignment; %rename (closeModel) CloseModel; +%rename (defaultSearchParameters) DefaultSearchParameters; +%rename (defaultModelParameters) DefaultModelParameters; // RoutingDimension methods. %rename (cumulVar) CumulVar; @@ -311,6 +319,17 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 %rename (setDimensionSpanCost) SetDimensionSpanCost; %rename (getDimensionSpanCost) GetDimensionSpanCost; +// Protobuf support +PROTO_INPUT(operations_research::RoutingSearchParameters, + com.google.ortools.constraintsolver.RoutingSearchParameters, + search_parameters) +PROTO_INPUT(operations_research::RoutingModelParameters, + com.google.ortools.constraintsolver.RoutingModelParameters, + parameters) +PROTO2_RETURN(operations_research::RoutingSearchParameters, + com.google.ortools.constraintsolver.RoutingSearchParameters) +PROTO2_RETURN(operations_research::RoutingModelParameters, + com.google.ortools.constraintsolver.RoutingModelParameters) // Wrap cp includes %include "constraint_solver/routing.h" diff --git a/src/constraint_solver/local_search.cc b/src/constraint_solver/local_search.cc index 5163700e7d..7bdd552575 100644 --- a/src/constraint_solver/local_search.cc +++ b/src/constraint_solver/local_search.cc @@ -109,8 +109,6 @@ void BaseLns::OnStart() { InitFragments(); } void BaseLns::InitFragments() {} -bool BaseLns::NextFragment() { return false; } - void BaseLns::AppendToFragment(int index) { if (index >= 0 && index < Size()) { fragment_.push_back(index); diff --git a/src/constraint_solver/model.proto b/src/constraint_solver/model.proto index 81b15eb57a..8b3130e154 100644 --- a/src/constraint_solver/model.proto +++ b/src/constraint_solver/model.proto @@ -12,89 +12,103 @@ // limitations under the License. -syntax = "proto2"; +syntax = "proto3"; import "constraint_solver/search_limit.proto"; package operations_research; -message CPIntegerMatrixProto { - optional int32 rows = 1; - optional int32 columns = 2; +message CpIntegerMatrix { + int32 rows = 1; + int32 columns = 2; repeated int64 values = 3; } // This message holds one argument of a constraint or expression. It // is referenced by the argument_name. Only one field apart the name // must be set. -message CPArgumentProto { - optional int32 argument_index = 1; - optional int64 integer_value = 2; +message CpArgument { + int32 argument_index = 1; + int64 integer_value = 2; repeated int64 integer_array = 3; - optional int32 integer_expression_index = 4; + int32 integer_expression_index = 4; repeated int32 integer_expression_array = 5; - optional int32 interval_index = 6; + int32 interval_index = 6; repeated int32 interval_array = 7; - optional int32 sequence_index = 8; + int32 sequence_index = 8; repeated int32 sequence_array = 9; - optional CPIntegerMatrixProto integer_matrix = 10; + CpIntegerMatrix integer_matrix = 10; + + enum Type { + UNDEFINED = 0; + INTEGER_VALUE = 1; + INTEGER_ARRAY = 2; + EXPRESSION = 3; + EXPRESSION_ARRAY = 4; + INTERVAL = 5; + INTERVAL_ARRAY = 6; + SEQUENCE = 7; + SEQUENCE_ARRAY = 8; + INTEGER_MATRIX = 9; + } + Type type = 11; } -message CPExtensionProto { - optional int32 type_index = 1; - repeated CPArgumentProto arguments = 2; +message CpExtension { + int32 type_index = 1; + repeated CpArgument arguments = 2; } -message CPIntegerExpressionProto { - optional int32 index = 1; - optional int32 type_index = 2; - optional string name = 3; - repeated CPArgumentProto arguments = 4; - repeated CPExtensionProto extensions = 5; +message CpIntegerExpression { + int32 index = 1; + int32 type_index = 2; + string name = 3; + repeated CpArgument arguments = 4; + repeated CpExtension extensions = 5; } -message CPIntervalVariableProto { - optional int32 index = 1; - optional int32 type_index = 2; - optional string name = 3; - repeated CPArgumentProto arguments = 4; +message CpIntervalVariable { + int32 index = 1; + int32 type_index = 2; + string name = 3; + repeated CpArgument arguments = 4; } -message CPSequenceVariableProto { - optional int32 index = 1; - optional int32 type_index = 2; - optional string name = 3; - repeated CPArgumentProto arguments = 4; +message CpSequenceVariable { + int32 index = 1; + int32 type_index = 2; + string name = 3; + repeated CpArgument arguments = 4; } -message CPConstraintProto { - optional int32 index = 1; - optional int32 type_index = 2; - optional string name = 3; - repeated CPArgumentProto arguments = 4; - repeated CPExtensionProto extensions = 5; +message CpConstraint { + int32 index = 1; + int32 type_index = 2; + string name = 3; + repeated CpArgument arguments = 4; + repeated CpExtension extensions = 5; } -message CPObjectiveProto { - optional bool maximize = 1; - optional int64 step = 2; - optional int32 objective_index = 3; +message CpObjective { + bool maximize = 1; + int64 step = 2; + int32 objective_index = 3; } -message CPVariableGroup { - repeated CPArgumentProto arguments = 1; - optional string type = 2; +message CpVariableGroup { + repeated CpArgument arguments = 1; + string type = 2; } -message CPModelProto { - optional string model = 1; - optional int32 version = 2; +message CpModel { + string model = 1; + int32 version = 2; repeated string tags = 3; - repeated CPIntegerExpressionProto expressions = 4; - repeated CPIntervalVariableProto intervals = 5; - repeated CPSequenceVariableProto sequences = 6; - repeated CPConstraintProto constraints = 7; - optional CPObjectiveProto objective = 8; - optional SearchLimitProto search_limit = 9; - repeated CPVariableGroup variable_groups = 10; - optional string license_text = 11; + repeated CpIntegerExpression expressions = 4; + repeated CpIntervalVariable intervals = 5; + repeated CpSequenceVariable sequences = 6; + repeated CpConstraint constraints = 7; + CpObjective objective = 8; + SearchLimitParameters search_limit = 9; + repeated CpVariableGroup variable_groups = 10; + string license_text = 11; } diff --git a/src/constraint_solver/python/constraint_solver.swig b/src/constraint_solver/python/constraint_solver.swig index c8a60ebf20..ef719e7506 100644 --- a/src/constraint_solver/python/constraint_solver.swig +++ b/src/constraint_solver/python/constraint_solver.swig @@ -32,13 +32,11 @@ %include "base/base.swig" +%include "util/python/proto.swig" + // PY_CONVERT_HELPER_* macros. %include "constraint_solver/python/constraint_solver_helpers.swig" -// Callback wrapping. See base/python/callbacks.swig. -#define FATAL_CALLBACK_EXCEPTION -%include "base/python/callbacks.swig" - // std::function utilities. %include "util/python/functions.swig" @@ -50,6 +48,15 @@ // The absence of whitespace before 'swiglint' is mandatory. //swiglint: disable swigtype-name +// We need to forward-declare the proto here, so that PROTO_INPUT involving it +// works correctly. The order matters very much: this declaration needs to be +// before the %{ #include ".../constraint_solver.h" %}. +namespace operations_research { +class AssignmentProto; +class ConstraintSolverParameters; +class SearchLimitParameters; +} // namespace operations_research + %{ #include // For FailureProtect. See below. @@ -61,6 +68,9 @@ struct FailureProtect { // This #includes constraint_solver.h, and inlines some C++ helpers. #include "constraint_solver/python/pywrapcp_util.h" +#include "constraint_solver/assignment.pb.h" +#include "constraint_solver/search_limit.pb.h" +#include "constraint_solver/solver_parameters.pb.h" %} // We need to fully support C++ inheritance, because it is heavily used by the @@ -78,8 +88,6 @@ struct FailureProtect { // // See the occurrences of "director" in this file. %module(directors="1") operations_research -#pragma SWIG nowarn=473 - // The %feature and %exception below let python exceptions that occur within // director method propagate to the user as they were originally. See // http://www.swig.org/Doc1.3/Python.html#Python_nn36 for example. @@ -170,6 +178,11 @@ static void PyFunctionSolverToVoid(PyObject* pyfunc, // ============= Extensions ============== // Add display methods on BaseObject and Solver. +%extend operations_research::BaseObject { + std::string __str__() { + return $self->DebugString(); + } +} %extend operations_research::Solver { std::string __str__() { return $self->DebugString(); @@ -269,6 +282,7 @@ PY_STRINGIFY_DEBUGSTRING(Decision); return $self->MakeElement(values, index); } + DecisionBuilder* VarEvalValStrPhase( const std::vector& vars, std::function var_evaluator, @@ -720,6 +734,8 @@ namespace operations_research { %unignore Solver::~Solver; %unignore Solver::AddConstraint; %unignore Solver::Solve; +%unignore Solver::DefaultSolverParameters; +%rename (Parameters) Solver::parameters; %unignore Solver::DemonPriority; %unignore Solver::DELAYED_PRIORITY; %unignore Solver::VAR_PRIORITY; @@ -899,7 +915,7 @@ namespace operations_research { %rename (ConstantRestart) Solver::MakeConstantRestart; // Solver: Search Limits. -%unignore Solver::SearchLimitProto; // search_limit.proto +%unignore Solver::SearchLimitParameters; // search_limit.proto %rename (Limit) Solver::MakeLimit; %rename (TimeLimit) Solver::MakeTimeLimit; %rename (BranchesLimit) Solver::MakeBranchesLimit; @@ -1153,38 +1169,47 @@ namespace operations_research { // - AssignmentElement // - SolutionPool -// SolverParameters -// Ignored: -// - Constants: -// - kDefaultTrailCompression -// - kDefaultTrailBlockSize -// - kDefaultArraySplitSize -// - kDefaultNameStoring -// - kDefaultProfileLevel -// - kDefaultTraceLevel -// - kDefaultNameAllVariables -%unignore SolverParameters; -%unignore SolverParameters::SolverParameters; +// ConstraintSolverParameters +%unignore ConstraintSolverParameters; +%unignore ConstraintSolverParameters::ConstraintSolverParameters; +%unignore ConstraintSolverParameters::~ConstraintSolverParameters; -// SolverParameters: Enums. -%unignore SolverParameters::TrailCompression; -%unignore SolverParameters::NO_COMPRESSION; -%unignore SolverParameters::COMPRESS_WITH_ZLIB; -%unignore SolverParameters::ProfileLevel; -%unignore SolverParameters::NO_PROFILING; -%unignore SolverParameters::NORMAL_PROFILING; -%unignore SolverParameters::TraceLevel; -%unignore SolverParameters::NO_TRACE; -%unignore SolverParameters::NORMAL_TRACE; +// ConstraintSolverParameters: Enums. +%unignore ConstraintSolverParameters::TrailCompression; +%unignore ConstraintSolverParameters::NO_COMPRESSION; +%unignore ConstraintSolverParameters::COMPRESS_WITH_ZLIB; -// SolverParameters: data members. -%unignore SolverParameters::compress_trail; -%unignore SolverParameters::trail_block_size; -%unignore SolverParameters::array_split_size; -%unignore SolverParameters::store_names; -%unignore SolverParameters::profile_level; -%unignore SolverParameters::trace_level; -%unignore SolverParameters::name_all_variables; +// ConstraintSolverParameters: methods. +%unignore ConstraintSolverParameters::compress_trail; +%unignore ConstraintSolverParameters::set_compress_trail; +%unignore ConstraintSolverParameters::trail_block_size; +%unignore ConstraintSolverParameters::set_trail_block_size; +%unignore ConstraintSolverParameters::array_split_size; +%unignore ConstraintSolverParameters::set_array_split_size; +%unignore ConstraintSolverParameters::store_names; +%unignore ConstraintSolverParameters::set_store_names; +%unignore ConstraintSolverParameters::name_cast_variables; +%unignore ConstraintSolverParameters::set_name_cast_variables; +%unignore ConstraintSolverParameters::name_all_variables; +%unignore ConstraintSolverParameters::set_name_all_variables; +%unignore ConstraintSolverParameters::profile_propagation; +%unignore ConstraintSolverParameters::set_profile_propagation; +%unignore ConstraintSolverParameters::profile_file; +%unignore ConstraintSolverParameters::set_profile_file; +%unignore ConstraintSolverParameters::trace_propagation; +%unignore ConstraintSolverParameters::set_trace_propagation; +%unignore ConstraintSolverParameters::trace_search; +%unignore ConstraintSolverParameters::set_trace_search; +%unignore ConstraintSolverParameters::print_model; +%unignore ConstraintSolverParameters::set_print_model; +%unignore ConstraintSolverParameters::print_model_stats; +%unignore ConstraintSolverParameters::set_print_model_stats; +%unignore ConstraintSolverParameters::print_added_constraints; +%unignore ConstraintSolverParameters::set_print_added_constraints; +%unignore ConstraintSolverParameters::export_file; +%unignore ConstraintSolverParameters::set_export_file; +%unignore ConstraintSolverParameters::disable_solve; +%unignore ConstraintSolverParameters::set_disable_solve; // DefaultPhaseParameters // Ignored: @@ -1296,7 +1321,6 @@ namespace operations_research { %unignore Constraint::Constraint; %unignore Constraint::~Constraint; %unignore Constraint::Post; -//%unignore Constraint::InitialPropagate; %rename (InitialPropagateWrapper) Constraint::InitialPropagate; %unignore Constraint::Var; %unignore Constraint::DebugString; @@ -1872,14 +1896,25 @@ namespace operations_research { } // namespace operations_research +PY_PROTO_TYPEMAP(ortools.constraint_solver.assignment_pb2, + AssignmentProto, + operations_research::AssignmentProto) +PY_PROTO_TYPEMAP(ortools.constraint_solver.solver_parameters_pb2, + ConstraintSolverParameters, + operations_research::ConstraintSolverParameters) +PY_PROTO_TYPEMAP(ortools.constraint_solver_search_limit_pb2, + SearchLimitParameters, + operations_research::SearchLimitParameters) + %include "constraint_solver/constraint_solver.h" + // Define templates instantiation after wrapping. namespace operations_research { %rename (RevInteger) Rev; %rename (RevInteger) Rev::Rev; -%unignore Rev::SetValue; %unignore Rev::Value; +%unignore Rev::SetValue; %template(RevInteger) Rev; %rename (NumericalRevInteger) NumericalRev; @@ -2011,9 +2046,9 @@ namespace operations_research { // BaseLns. +%unignore BaseLns; %feature("director") BaseLns; %unignore BaseLns::BaseLns; -%unignore BaseLns; %unignore BaseLns::~BaseLns; %unignore BaseLns::InitFragments; %unignore BaseLns::NextFragment; diff --git a/src/constraint_solver/python/routing.swig b/src/constraint_solver/python/routing.swig index f30541ad98..c00ed05ed7 100644 --- a/src/constraint_solver/python/routing.swig +++ b/src/constraint_solver/python/routing.swig @@ -15,16 +15,23 @@ %include "constraint_solver/python/constraint_solver.swig" +// TODO(user): remove this when we no longer use callbacks in the routing. +#define FATAL_CALLBACK_EXCEPTION +%include "base/python/callbacks.swig" + +// We need to forward-declare the proto here, so that PROTO_INPUT involving it +// works correctly. The order matters very much: this declaration needs to be +// before the %{ #include ".../routing.h" %}. +namespace operations_research { +class RoutingModelParameters; +class RoutingSearchParameters; +} // namespace operations_research + // Include the file we want to wrap a first time. %{ #include "constraint_solver/routing.h" %} -// See ./constraint_solver_helpers.swig. -//PY_CONVERT_HELPER_PTR(SearchMonitor); -//PY_CONVERT_HELPER_PTR(IntervalVar); -//PY_CONVERT_HELPER_INTEXPR_OR_INTVAR(IntVar); - // Convert RoutingModel::NodeIndex to (32-bit signed) integers. %typemap(in) operations_research::RoutingModel::NodeIndex { $1 = operations_research::RoutingModel::NodeIndex(PyInt_AsLong($input)); @@ -170,10 +177,17 @@ PY_LIST_OUTPUT_TYPEMAP(operations_research::RoutingModel::NodeEvaluator2*, const std::string& name) { DCHECK_EQ(values.size(), $self->nodes()); $self->AddVectorDimension(values.data(), capacity, - fix_start_cumul_to_zero, name); + fix_start_cumul_to_zero, name); } } +PY_PROTO_TYPEMAP(ortools.constraint_solver.routing_parameters_pb2, + RoutingModelParameters, + operations_research::RoutingModelParameters) +PY_PROTO_TYPEMAP(ortools.constraint_solver.routing_parameters_pb2, + RoutingSearchParameters, + operations_research::RoutingSearchParameters) + %ignore operations_research::RoutingModel::WrapIndexEvaluator( Solver::IndexEvaluator2* evaluator); @@ -181,5 +195,10 @@ PY_LIST_OUTPUT_TYPEMAP(operations_research::RoutingModel::NodeEvaluator2*, int nodes, int vehicles, const std::vector >& start_end); +%ignore operations_research::RoutingModel::RoutingModel( + int nodes, int vehicles, + const std::vector >& start_end, + const RoutingModelParameters& parameters); + // Wrap cp includes %include "constraint_solver/routing.h" diff --git a/src/constraint_solver/routing.cc b/src/constraint_solver/routing.cc index de8beaa0a4..a3cb4002c8 100644 --- a/src/constraint_solver/routing.cc +++ b/src/constraint_solver/routing.cc @@ -27,6 +27,7 @@ #include "base/commandlineflags.h" #include "base/integral_types.h" #include "base/logging.h" +#include "google/protobuf/text_format.h" #include "base/map_util.h" #include "base/stl_util.h" #include "base/thorough_hash.h" @@ -38,76 +39,7 @@ namespace operations_research { class LocalSearchPhaseParameters; } // namespace operations_research -// Neighborhood deactivation -// TODO(user): move (most of?) these flags into a parameter-driven API. -DEFINE_bool(routing_no_lns, false, - "Routing: forbids use of Large Neighborhood Search."); -DEFINE_bool(routing_no_fullpathlns, true, - "Routing: forbids use of Full-path Large Neighborhood Search."); -DEFINE_bool(routing_no_relocate, false, - "Routing: forbids use of Relocate neighborhood."); -DEFINE_bool(routing_no_relocate_neighbors, true, - "Routing: forbids use of RelocateNeighbors neighborhood."); -DEFINE_bool(routing_no_exchange, false, - "Routing: forbids use of Exchange neighborhood."); -DEFINE_bool(routing_no_cross, false, - "Routing: forbids use of Cross neighborhood."); -DEFINE_bool(routing_no_2opt, false, - "Routing: forbids use of 2Opt neighborhood."); -DEFINE_bool(routing_no_oropt, false, - "Routing: forbids use of OrOpt neighborhood."); -DEFINE_bool(routing_no_make_active, false, - "Routing: forbids use of MakeActive/SwapActive/MakeInactive " - "neighborhoods."); -DEFINE_bool(routing_no_lkh, false, "Routing: forbids use of LKH neighborhood."); -DEFINE_bool(routing_no_tsp, true, - "Routing: forbids use of TSPOpt neighborhood."); -DEFINE_bool(routing_no_tsplns, true, - "Routing: forbids use of TSPLNS neighborhood."); -DEFINE_bool(routing_use_chain_make_inactive, false, - "Routing: use chain version of MakeInactive neighborhood."); -DEFINE_bool(routing_use_extended_swap_active, false, - "Routing: use extended version of SwapActive neighborhood."); - -// Search limits -DEFINE_int64(routing_solution_limit, kint64max, - "Routing: number of solutions limit."); -DEFINE_int64(routing_time_limit, kint64max, "Routing: time limit in ms."); -DEFINE_int64(routing_lns_time_limit, 100, - "Routing: time limit in ms for LNS sub-decisionbuilder."); - -// Meta-heuritics -DEFINE_bool(routing_guided_local_search, false, "Routing: use GLS."); -DEFINE_double(routing_guided_local_search_lambda_coefficient, 0.1, - "Lambda coefficient in GLS."); -DEFINE_bool(routing_simulated_annealing, false, - "Routing: use simulated annealing."); -DEFINE_bool(routing_tabu_search, false, "Routing: use tabu search."); - -// Search control -DEFINE_bool(routing_dfs, false, "Routing: use a complete depth-first search."); -DEFINE_string(routing_first_solution, "", - "Routing: first solution heuristic. See RoutingStrategyName() " - "in the code to get a full list."); -DEFINE_bool(routing_use_first_solution_dive, false, - "Dive (left-branch) for first solution."); -DEFINE_int64(routing_optimization_step, 1, "Optimization step."); -DEFINE_bool(routing_use_filtered_first_solutions, true, - "Use filtered version of first solution heuristics if available."); - -// Filtering control -DEFINE_bool(routing_use_objective_filter, true, - "Use objective filter to speed up local search."); -DEFINE_bool(routing_use_path_cumul_filter, true, - "Use PathCumul constraint filter to speed up local search."); -DEFINE_bool(routing_use_pickup_and_delivery_filter, true, - "Use filter which filters precedence and same route constraints."); -DEFINE_bool(routing_use_vehicle_var_filter, true, - "Use filter which filters vehicle variable domains."); -DEFINE_bool(routing_use_disjunction_filter, true, - "Use filter which filters node disjunction constraints."); - -// First solution heuristics +// TODO(user): Remove these flags after cleaning up the Savings heuristic. DEFINE_double(savings_route_shape_parameter, 1.0, "Coefficient of the added arc in the savings definition." "Variation of this parameter may provide heuristic solutions " @@ -125,25 +57,20 @@ DEFINE_int64(sweep_sectors, 1, "The number of sectors the space is divided before it is sweeped " "by the ray."); -// Propagation control -DEFINE_bool(routing_use_light_propagation, true, - "Use constraints with light propagation in routing model."); - -// Misc +// Cache settings. +// TODO(user): Investigate if these settings could be moved to +// RoutingSearchParameters or if we can get rid of them entirely. DEFINE_bool(routing_cache_callbacks, false, "Cache callback calls."); DEFINE_int64(routing_max_cache_size, 1000, "Maximum cache size when callback caching is on."); + +// Trace settings DEFINE_bool(routing_trace, false, "Routing: trace search."); DEFINE_bool(routing_search_trace, false, "Routing: use SearchTrace for monitoring search."); -DEFINE_bool(routing_use_homogeneous_costs, true, - "Routing: use homogeneous cost model when possible."); -DEFINE_bool(routing_check_compact_assignment, true, - "Routing::CompactAssignment calls Solver::CheckAssignment on the " - "compact assignment."); -DEFINE_bool(routing_fingerprint_arc_cost_evaluators, true, - "Compare arc-cost evaluators using the fingerprint of their " - "corresponding matrix instead of evaluator addresses."); + +// TODO(user): Move most of the following settings to a model parameter +// proto. #if defined(_MSC_VER) namespace stdext { @@ -275,6 +202,41 @@ Constraint* MakeLightElement2(Solver* const solver, IntVar* const var, solver, var, index1, index2, std::move(values))); } +// PathOperator subclass storing current previous nodes. +// TODO(user): Move this to constraint_solveri.h and/or merge with +// PathOperator. +class PathWithPreviousNodesOperator : public PathOperator { + public: + PathWithPreviousNodesOperator( + const std::vector& vars, const std::vector& secondary_vars, + int number_of_base_nodes, + ResultCallback1* start_empty_path_class) + : PathOperator(vars, secondary_vars, number_of_base_nodes, + start_empty_path_class) { + int64 max_next = -1; + for (const IntVar* const var : vars) { + max_next = std::max(max_next, var->Max()); + } + prevs_.resize(max_next + 1, -1); + } + ~PathWithPreviousNodesOperator() override {} + int64 Prev(int64 node_index) const { + DCHECK(!IsPathStart(node_index)); + return prevs_[node_index]; + } + bool IsPathStart(int64 node_index) const { return prevs_[node_index] == -1; } + + protected: + void OnNodeInitialization() override { + for (int node_index = 0; node_index < number_of_nexts(); ++node_index) { + prevs_[Next(node_index)] = node_index; + } + } + + private: + std::vector prevs_; +}; + // Relocate neighborhood which moves chains of neighbors. // The operator starts by relocating a node n after a node m, then continues // moving nodes which were after n as long as the "cost" added is less than @@ -298,20 +260,15 @@ Constraint* MakeLightElement2(Solver* const solver, IntVar* const var, // TODO(user): move this to local_search.cc and see if it can be merged with // standard Relocate. -class MakeRelocateNeighborsOperator : public PathOperator { +class MakeRelocateNeighborsOperator : public PathWithPreviousNodesOperator { public: MakeRelocateNeighborsOperator( const std::vector& vars, const std::vector& secondary_vars, ResultCallback1* start_empty_path_class, RoutingModel::TransitEvaluator2* arc_evaluator) - : PathOperator(vars, secondary_vars, 2, start_empty_path_class), - arc_evaluator_(arc_evaluator) { - int64 max_next = -1; - for (const IntVar* const var : vars) { - max_next = std::max(max_next, var->Max()); - } - prevs_.resize(max_next + 1, -1); - } + : PathWithPreviousNodesOperator(vars, secondary_vars, 2, + start_empty_path_class), + arc_evaluator_(arc_evaluator) {} ~MakeRelocateNeighborsOperator() override {} bool MakeNeighbor() override { const int64 before_chain = BaseNode(0); @@ -335,12 +292,6 @@ class MakeRelocateNeighborsOperator : public PathOperator { std::string DebugString() const override { return "RelocateNeighbors"; } private: - void OnNodeInitialization() override { - for (int i = 0; i < number_of_nexts(); ++i) { - prevs_[Next(i)] = i; - } - } - // Moves a chain starting after 'before_chain' and ending at 'chain_end' // after node 'destination'. Tries to repair the resulting solution by // checking if the new arc created after 'destination' is compatible with @@ -350,14 +301,16 @@ class MakeRelocateNeighborsOperator : public PathOperator { bool MoveChainAndRepair(int64 before_chain, int64 chain_end, int64 destination) { if (MoveChain(before_chain, chain_end, destination)) { - int64 current = prevs_[destination]; - int64 last = chain_end; - if (current == last) { // chain was just before destination - current = before_chain; - } - while (last >= 0 && current >= 0) { - last = Reposition(current, last); - current = prevs_[current]; + if (!IsPathStart(destination)) { + int64 current = Prev(destination); + int64 last = chain_end; + if (current == last) { // chain was just before destination + current = before_chain; + } + while (last >= 0 && !IsPathStart(current)) { + last = Reposition(current, last); + current = Prev(current); + } } return true; } @@ -392,7 +345,6 @@ class MakeRelocateNeighborsOperator : public PathOperator { } std::unique_ptr arc_evaluator_; - std::vector prevs_; }; LocalSearchOperator* MakeRelocateNeighbors( @@ -612,6 +564,192 @@ LocalSearchOperator* MakePairRelocate( vars, secondary_vars, start_empty_path_class, pairs)); } +// Operator which inserts inactive nodes into a path and makes a pair of +// inactive nodes inactive. +class NodePairSwapActiveOperator : public PathWithPreviousNodesOperator { + public: + NodePairSwapActiveOperator( + const std::vector& vars, const std::vector& secondary_vars, + ResultCallback1* start_empty_path_class, + const RoutingModel::NodePairs& node_pairs) + : PathWithPreviousNodesOperator(vars, secondary_vars, 1, + start_empty_path_class), + inactive_node_(0) { + int64 max_pair_index = -1; + for (const std::pair& node_pair : node_pairs) { + max_pair_index = std::max(max_pair_index, node_pair.first); + max_pair_index = std::max(max_pair_index, node_pair.second); + } + pairs_.resize(max_pair_index + 1, -1); + for (const std::pair& node_pair : node_pairs) { + pairs_[node_pair.first] = node_pair.second; + pairs_[node_pair.second] = node_pair.first; + } + } + ~NodePairSwapActiveOperator() override {} + std::string DebugString() const override { return "NodePairSwapActiveOperator"; } + bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; + bool MakeNeighbor() override; + + private: + void OnNodeInitialization() override; + + int inactive_node_; + std::vector pairs_; +}; + +void NodePairSwapActiveOperator::OnNodeInitialization() { + PathWithPreviousNodesOperator::OnNodeInitialization(); + for (int i = 0; i < Size(); ++i) { + if (IsInactive(i) && i < pairs_.size() && pairs_[i] == -1) { + inactive_node_ = i; + return; + } + } + inactive_node_ = Size(); +} + +bool NodePairSwapActiveOperator::MakeNextNeighbor(Assignment* delta, + Assignment* deltadelta) { + while (inactive_node_ < Size()) { + if (!IsInactive(inactive_node_) || + !PathOperator::MakeNextNeighbor(delta, deltadelta)) { + ResetPosition(); + ++inactive_node_; + } else { + return true; + } + } + return false; +} + +bool NodePairSwapActiveOperator::MakeNeighbor() { + const int64 base = BaseNode(0); + if (IsPathEnd(base)) { + return false; + } + const int64 next = Next(base); + if (next < pairs_.size() && pairs_[next] != -1) { + return MakeChainInactive(Prev(pairs_[next]), pairs_[next]) && + MakeChainInactive(base, next) && MakeActive(inactive_node_, base); + } + return false; +} + +LocalSearchOperator* NodePairSwapActive( + Solver* const solver, const std::vector& vars, + const std::vector& secondary_vars, + ResultCallback1* start_empty_path_class, + const RoutingModel::NodePairs& pairs) { + return solver->RevAlloc(new NodePairSwapActiveOperator( + vars, secondary_vars, start_empty_path_class, pairs)); +} + +// Operator which inserts pairs of inactive nodes into a path and makes an +// active node inactive. +// There are two versions: +// - one which makes inactive the node being replaced by the first node of the +// pair (with swap_first true); +// - one which makes inactive the node being replaced by the second node of the +// pair (with swap_first false). +template +class PairNodeSwapActiveOperator : public PathOperator { + public: + PairNodeSwapActiveOperator( + const std::vector& vars, const std::vector& secondary_vars, + ResultCallback1* start_empty_path_class, + const RoutingModel::NodePairs& node_pairs) + : PathOperator(vars, secondary_vars, 2, start_empty_path_class), + inactive_pair_(0), + pairs_(node_pairs) {} + ~PairNodeSwapActiveOperator() override {} + std::string DebugString() const override { return "PairNodeSwapActiveOperator"; } + bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; + bool MakeNeighbor() override; + + protected: + bool OnSamePathAsPreviousBase(int64 base_index) override { + // Both base nodes have to be on the same path since they represent the + // nodes after which unactive node pairs will be moved. + return true; + } + int64 GetBaseNodeRestartPosition(int base_index) override { + // Base node 1 must be after base node 0 if they are both on the same path. + if (base_index == 0 || StartNode(base_index) != StartNode(base_index - 1)) { + return StartNode(base_index); + } else { + return BaseNode(base_index - 1); + } + } + // Required to ensure that after synchronization the operator is in a state + // compatible with GetBaseNodeRestartPosition. + bool RestartAtPathStartOnSynchronize() override { return true; } + + private: + void OnNodeInitialization() override; + + int inactive_pair_; + RoutingModel::NodePairs pairs_; +}; + +template +void PairNodeSwapActiveOperator::OnNodeInitialization() { + for (int i = 0; i < pairs_.size(); ++i) { + if (IsInactive(pairs_[i].first) && IsInactive(pairs_[i].second)) { + inactive_pair_ = i; + return; + } + } + inactive_pair_ = pairs_.size(); +} + +template +bool PairNodeSwapActiveOperator::MakeNextNeighbor( + Assignment* delta, Assignment* deltadelta) { + while (inactive_pair_ < pairs_.size()) { + if (!IsInactive(pairs_[inactive_pair_].first) || + !IsInactive(pairs_[inactive_pair_].second) || + !PathOperator::MakeNextNeighbor(delta, deltadelta)) { + ResetPosition(); + ++inactive_pair_; + } else { + return true; + } + } + return false; +} + +template +bool PairNodeSwapActiveOperator::MakeNeighbor() { + const int64 base = BaseNode(0); + if (IsPathEnd(base)) { + return false; + } + const int64 pair_first = pairs_[inactive_pair_].first; + const int64 pair_second = pairs_[inactive_pair_].second; + if (swap_first) { + return MakeActive(pair_second, BaseNode(1)) && + MakeActive(pair_first, base) && + MakeChainInactive(pair_first, Next(pair_first)); + } else { + return MakeActive(pair_second, BaseNode(1)) && + MakeActive(pair_first, base) && + MakeChainInactive(pair_second, Next(pair_second)); + } +} + +LocalSearchOperator* PairNodeSwapActive( + Solver* const solver, const std::vector& vars, + const std::vector& secondary_vars, + ResultCallback1* start_empty_path_class, + const RoutingModel::NodePairs& pairs) { + return solver->ConcatenateOperators( + {solver->RevAlloc(new PairNodeSwapActiveOperator( + vars, secondary_vars, start_empty_path_class, pairs)), + solver->RevAlloc(new PairNodeSwapActiveOperator( + vars, secondary_vars, start_empty_path_class, pairs))}); +} + // Cached callbacks class RoutingCache : public RoutingModel::NodeEvaluator2 { @@ -624,12 +762,12 @@ class RoutingCache : public RoutingModel::NodeEvaluator2 { // directly, but through RoutingModel::NewCachedCallback that ensures that the // base callback is deleted properly. RoutingCache(RoutingModel::NodeEvaluator2* callback, int size) - : cached_(size), cache_(size), callback_(callback) { + : cached_(size), cache_(size), callback_(CHECK_NOTNULL(callback)) { for (RoutingModel::NodeIndex i(0); i < RoutingModel::NodeIndex(size); ++i) { cached_[i].resize(size, false); cache_[i].resize(size, 0); } - callback->CheckIsRepeatable(); + CHECK(callback->IsRepeatable()); } bool IsRepeatable() const override { return true; } int64 Run(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) override { @@ -656,6 +794,54 @@ class RoutingCache : public RoutingModel::NodeEvaluator2 { RoutingModel::NodeEvaluator2* const callback_; }; +class StateDependentRoutingCache : public RoutingModel::VariableNodeEvaluator2 { + public: + // Creates a new cached callback based on 'callback'. The cache object does + // not take ownership of the callback; the user must ensure that the callback + // gets deleted when it or the cache is no longer used. + // + // When used in the RoutingModel class, the constructor should not be called + // directly, but through RoutingModel::NewCachedStateDependentCallback that + // ensures that the base callback is deleted properly. + StateDependentRoutingCache(RoutingModel::VariableNodeEvaluator2* callback, + int size) + : cache_(size), callback_(CHECK_NOTNULL(callback)) { + for (RoutingModel::NodeIndex i(0); i < RoutingModel::NodeIndex(size); ++i) { + cache_[i].resize(size, {nullptr, nullptr}); + } + CHECK(callback->IsRepeatable()); + } + ~StateDependentRoutingCache() override { + hash_set value_functions_delete; + hash_set index_functions_delete; + for (const auto& cache_line : cache_) { + for (const RoutingModel::StateDependentTransit& transit : cache_line) { + value_functions_delete.insert(transit.transit); + index_functions_delete.insert(transit.transit_plus_identity); + } + } + STLDeleteElements(&value_functions_delete); + STLDeleteElements(&index_functions_delete); + } + bool IsRepeatable() const override { return true; } + // This method returns cached results of the callback. + RoutingModel::StateDependentTransit Run(RoutingModel::NodeIndex i, + RoutingModel::NodeIndex j) override { + RoutingModel::StateDependentTransit& cache_cell = cache_[i][j]; + if (cache_cell.transit == nullptr) { + cache_cell = callback_->Run(i, j); + } + return cache_cell; + } + + private: + ITIVector< + RoutingModel::NodeIndex, + ITIVector> + cache_; + RoutingModel::VariableNodeEvaluator2* const callback_; +}; + // Evaluators class MatrixEvaluator : public BaseObject { @@ -705,21 +891,42 @@ int64 VectorEvaluator::Value(RoutingModel::NodeIndex i, return values_[i.value()]; } +template class ConstantEvaluator : public BaseObject { public: - explicit ConstantEvaluator(int64 value) : value_(value) {} + explicit ConstantEvaluator(T value) : value_(value) {} ~ConstantEvaluator() override {} - int64 Value(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { + T Value(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { return value_; } + static ResultCallback2* + MakeNodeEvaluatorCallback(T value, Solver* solver) { + const ConstantEvaluator* const constant_evaluator = + solver->RevAlloc(new ConstantEvaluator(value)); + return constant_evaluator->MakeNodeEvaluatorCallback(); + } + + ResultCallback2* + MakeNodeEvaluatorCallback() const { + return NewPermanentCallback(this, &ConstantEvaluator::Value); + } + private: - const int64 value_; + const T value_; }; -// Left-branch dive branch selector - -Solver::DecisionModification LeftDive() { return Solver::KEEP_LEFT; } +ConstraintSolverParameters::TrailCompression GetTrailCompression( + RoutingModelParameters::TrailCompression trail_compression) { + switch (trail_compression) { + case RoutingModelParameters::NONE: + return ConstraintSolverParameters::NO_COMPRESSION; + case RoutingModelParameters::ZLIB: + return ConstraintSolverParameters::COMPRESS_WITH_ZLIB; +default: + return ConstraintSolverParameters::NO_COMPRESSION; + } +} } // namespace @@ -735,13 +942,11 @@ const RoutingModel::DisjunctionIndex RoutingModel::kNoDisjunction(-1); const RoutingModel::DimensionIndex RoutingModel::kNoDimension(-1); -void RoutingModel::SetGlobalParameters(const RoutingParameters& p) { - FLAGS_routing_use_light_propagation = p.use_light_propagation; - FLAGS_routing_cache_callbacks = p.cache_callbacks; - FLAGS_routing_max_cache_size = p.max_cache_size; -} - RoutingModel::RoutingModel(int nodes, int vehicles) + : RoutingModel(nodes, vehicles, DefaultModelParameters()) {} + +RoutingModel::RoutingModel(int nodes, int vehicles, + const RoutingModelParameters& parameters) : nodes_(nodes), vehicles_(vehicles), no_cycle_constraint_(nullptr), @@ -751,7 +956,7 @@ RoutingModel::RoutingModel(int nodes, int vehicles) cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)), cost_classes_(), costs_are_homogeneous_across_vehicles_( - FLAGS_routing_use_homogeneous_costs), + parameters.reduce_vehicle_cost_model()), vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)), starts_(vehicles), ends_(vehicles), @@ -759,26 +964,31 @@ RoutingModel::RoutingModel(int nodes, int vehicles) is_depot_set_(false), closed_(false), status_(ROUTING_NOT_SOLVED), - first_solution_strategy_(ROUTING_DEFAULT_STRATEGY), - metaheuristic_(ROUTING_GREEDY_DESCENT), collect_assignments_(nullptr), solve_db_(nullptr), improve_db_(nullptr), restore_assignment_(nullptr), assignment_(nullptr), preassignment_(nullptr), - time_limit_ms_(FLAGS_routing_time_limit), - lns_time_limit_ms_(FLAGS_routing_lns_time_limit), limit_(nullptr), ls_limit_(nullptr), lns_limit_(nullptr) { - SolverParameters parameters; - solver_.reset(new Solver("Routing", parameters)); + VLOG(1) << "Model parameters:\n" << parameters.DebugString(); + ConstraintSolverParameters solver_parameters = + Solver::DefaultSolverParameters(); + solver_parameters.set_compress_trail( + GetTrailCompression(parameters.trail_compression())); + solver_.reset(new Solver("Routing", solver_parameters)); Initialize(); } RoutingModel::RoutingModel(int nodes, int vehicles, const std::vector>& start_ends) + : RoutingModel(nodes, vehicles, start_ends, DefaultModelParameters()) {} + +RoutingModel::RoutingModel(int nodes, int vehicles, + const std::vector>& start_ends, + const RoutingModelParameters& parameters) : nodes_(nodes), vehicles_(vehicles), no_cycle_constraint_(nullptr), @@ -788,28 +998,28 @@ RoutingModel::RoutingModel(int nodes, int vehicles, cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)), cost_classes_(), costs_are_homogeneous_across_vehicles_( - FLAGS_routing_use_homogeneous_costs), + parameters.reduce_vehicle_cost_model()), vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)), starts_(vehicles), ends_(vehicles), is_depot_set_(false), closed_(false), status_(ROUTING_NOT_SOLVED), - first_solution_strategy_(ROUTING_DEFAULT_STRATEGY), - metaheuristic_(ROUTING_GREEDY_DESCENT), collect_assignments_(nullptr), solve_db_(nullptr), improve_db_(nullptr), restore_assignment_(nullptr), assignment_(nullptr), preassignment_(nullptr), - time_limit_ms_(FLAGS_routing_time_limit), - lns_time_limit_ms_(FLAGS_routing_lns_time_limit), limit_(nullptr), ls_limit_(nullptr), lns_limit_(nullptr) { - SolverParameters parameters; - solver_.reset(new Solver("Routing", parameters)); + VLOG(1) << "Model parameters:\n" << parameters.DebugString(); + ConstraintSolverParameters solver_parameters = + Solver::DefaultSolverParameters(); + solver_parameters.set_compress_trail( + GetTrailCompression(parameters.trail_compression())); + solver_.reset(new Solver("Routing", solver_parameters)); CHECK_EQ(vehicles, start_ends.size()); hash_set depot_set; for (const std::pair start_end : start_ends) { @@ -824,6 +1034,12 @@ RoutingModel::RoutingModel(int nodes, int vehicles, RoutingModel::RoutingModel(int nodes, int vehicles, const std::vector& starts, const std::vector& ends) + : RoutingModel(nodes, vehicles, starts, ends, DefaultModelParameters()) {} + +RoutingModel::RoutingModel(int nodes, int vehicles, + const std::vector& starts, + const std::vector& ends, + const RoutingModelParameters& parameters) : nodes_(nodes), vehicles_(vehicles), no_cycle_constraint_(nullptr), @@ -833,28 +1049,28 @@ RoutingModel::RoutingModel(int nodes, int vehicles, cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)), cost_classes_(), costs_are_homogeneous_across_vehicles_( - FLAGS_routing_use_homogeneous_costs), + parameters.reduce_vehicle_cost_model()), vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)), starts_(vehicles), ends_(vehicles), is_depot_set_(false), closed_(false), status_(ROUTING_NOT_SOLVED), - first_solution_strategy_(ROUTING_DEFAULT_STRATEGY), - metaheuristic_(ROUTING_GREEDY_DESCENT), collect_assignments_(nullptr), solve_db_(nullptr), improve_db_(nullptr), restore_assignment_(nullptr), assignment_(nullptr), preassignment_(nullptr), - time_limit_ms_(FLAGS_routing_time_limit), - lns_time_limit_ms_(FLAGS_routing_lns_time_limit), limit_(nullptr), ls_limit_(nullptr), lns_limit_(nullptr) { - SolverParameters parameters; - solver_.reset(new Solver("Routing", parameters)); + VLOG(1) << "Model parameters:\n" << parameters.DebugString(); + ConstraintSolverParameters solver_parameters = + Solver::DefaultSolverParameters(); + solver_parameters.set_compress_trail( + GetTrailCompression(parameters.trail_compression())); + solver_.reset(new Solver("Routing", solver_parameters)); CHECK_EQ(vehicles, starts.size()); CHECK_EQ(vehicles, ends.size()); hash_set depot_set; @@ -898,8 +1114,62 @@ void RoutingModel::Initialize() { RoutingModel::~RoutingModel() { STLDeleteElements(&owned_node_callbacks_); - STLDeleteElements(&owned_index_callbacks_); STLDeleteElements(&dimensions_); + STLDeleteElements(&owned_state_dependent_callbacks_); +} + +RoutingModelParameters RoutingModel::DefaultModelParameters() { + static const char* const kModelParameters = + "reduce_vehicle_cost_model: true " + "trail_compression: ZLIB"; + RoutingModelParameters parameters; + if (!google::protobuf::TextFormat::ParseFromString(kModelParameters, ¶meters)) { + LOG(ERROR) << "Unsupported default model parameters: " << kModelParameters; + } + return parameters; +} + +RoutingSearchParameters RoutingModel::DefaultSearchParameters() { + static const char* const kSearchParameters = + "first_solution_strategy: AUTOMATIC " + "use_filtered_first_solution_strategy: false " + "local_search_operators {" + " use_relocate: true" + " use_relocate_pair: true" + " use_relocate_neighbors: false" + " use_exchange: true" + " use_cross: true" + " use_cross_exchange: false" + " use_two_opt: true" + " use_or_opt: true" + " use_lin_kernighan: true" + " use_tsp_opt: false" + " use_make_active: true" + " use_make_inactive: true" + " use_make_chain_inactive: false" + " use_swap_active: true" + " use_extended_swap_active: false" + " use_node_pair_swap_active: true" + " use_path_lns: false" + " use_full_path_lns: false" + " use_tsp_lns: false" + " use_inactive_lns: false" + "}" + "local_search_metaheuristic: AUTOMATIC " + "guided_local_search_lambda_coefficient: 0.1 " + "use_depth_first_search: false " + "optimization_step: 1 " + "solution_limit: 0x7FFFFFFFFFFFFFFF " // kint64max + "time_limit_ms: 0x7FFFFFFFFFFFFFFF " // kint64max + "lns_time_limit_ms: 100 " + "use_light_propagation: true " + "fingerprint_arc_cost_evaluators: true "; + RoutingSearchParameters parameters; + if (!google::protobuf::TextFormat::ParseFromString(kSearchParameters, ¶meters)) { + LOG(ERROR) << "Unsupported default search parameters: " + << kSearchParameters; + } + return parameters; } void RoutingModel::AddNoCycleConstraintInternal() { @@ -950,19 +1220,40 @@ bool RoutingModel::AddDimensionWithCapacityInternal( const std::vector& evaluators, int64 slack_max, int64 capacity, VehicleEvaluator* vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& dimension_name) { + return InitializeDimensionInternal( + evaluators, std::vector(), slack_max, capacity, + vehicle_capacity, fix_start_cumul_to_zero, + new RoutingDimension(this, dimension_name, nullptr)); +} + +bool RoutingModel::InitializeDimensionInternal( + const std::vector& evaluators, + const std::vector& state_dependent_evaluators, + int64 slack_max, int64 capacity, VehicleEvaluator* vehicle_capacity, + bool fix_start_cumul_to_zero, RoutingDimension* dimension) { + CHECK(dimension != nullptr); CheckDepot(); - if (!HasDimension(dimension_name)) { + CHECK_EQ(vehicles_, evaluators.size()); + CHECK((dimension->base_dimension_ == nullptr && + state_dependent_evaluators.empty()) || + vehicles_ == state_dependent_evaluators.size()); + if (!HasDimension(dimension->name())) { const DimensionIndex dimension_index(dimensions_.size()); - dimension_name_to_index_[dimension_name] = dimension_index; - dimensions_.push_back(new RoutingDimension(this, dimension_name)); - RoutingDimension* const dimension = dimensions_[dimension_index]; + dimension_name_to_index_[dimension->name()] = dimension_index; + dimensions_.push_back(dimension); std::vector cached_evaluators; for (NodeEvaluator2* const evaluator : evaluators) { CHECK(evaluator != nullptr); cached_evaluators.push_back(NewCachedCallback(evaluator)); } + std::vector cached_state_dependent_evaluators; + for (VariableNodeEvaluator2* const evaluator : state_dependent_evaluators) { + CHECK(evaluator != nullptr); + cached_state_dependent_evaluators.push_back( + NewCachedStateDependentCallback(evaluator)); + } dimension->Initialize(vehicle_capacity, capacity, cached_evaluators, - slack_max); + cached_state_dependent_evaluators, slack_max); solver_->AddConstraint(solver_->MakeDelayedPathCumul( nexts_, active_, dimension->cumuls(), dimension->transits())); if (fix_start_cumul_to_zero) { @@ -974,11 +1265,13 @@ bool RoutingModel::AddDimensionWithCapacityInternal( } return true; } else { + delete dimension; hash_set evaluator_set(evaluators.begin(), evaluators.end()); - for (NodeEvaluator2* const evaluator : evaluator_set) { - delete evaluator; - } + STLDeleteElements(&evaluator_set); + hash_set dependent_evaluator_set( + state_dependent_evaluators.begin(), state_dependent_evaluators.end()); + STLDeleteElements(&dependent_evaluator_set); delete vehicle_capacity; return false; } @@ -988,11 +1281,9 @@ 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), slack_max, - capacity, fix_start_cumul_to_zero, dimension_name); + ConstantEvaluator::MakeNodeEvaluatorCallback(value, solver_.get()), + slack_max, capacity, fix_start_cumul_to_zero, dimension_name); } bool RoutingModel::AddVectorDimension(const int64* values, int64 capacity, @@ -1014,12 +1305,158 @@ bool RoutingModel::AddMatrixDimension(const int64* const* values, 0, capacity, fix_start_cumul_to_zero, dimension_name); } -void RoutingModel::GetAllDimensions(std::vector* dimension_names) const { - CHECK(dimension_names != nullptr); - dimension_names->clear(); - for (const auto& dimension_name_index : dimension_name_to_index_) { - dimension_names->push_back(dimension_name_index.first); +namespace { +template +static int64 ReturnZero(A a, B b) { + return 0; +} + +// RangeMakeElementExpr is an IntExpr that corresponds to a +// RangeIntToIntFunction indexed by an IntVar. +// Do not create this class dicretly, but rather use MakeRangeMakeElementExpr. +class RangeMakeElementExpr : public BaseIntExpr { + public: + RangeMakeElementExpr(const RangeIntToIntFunction* callback, IntVar* index, + Solver* s) + : BaseIntExpr(s), callback_(CHECK_NOTNULL(callback)), index_(index) { + CHECK(callback_ != nullptr); + CHECK(index != nullptr); } + + int64 Min() const override { + // Converting [index_->Min(), index_->Max()] to [idx_min, idx_max). + const int idx_min = index_->Min(); + const int idx_max = index_->Max() + 1; + return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max) + : kint64max; + } + void SetMin(int64 new_min) override { + const int64 old_min = Min(); + const int64 old_max = Max(); + if (old_min < new_min && new_min <= old_max) { + const int64 old_idx_min = index_->Min(); + const int64 old_idx_max = index_->Max() + 1; + if (old_idx_min < old_idx_max) { + const int64 new_idx_min = callback_->RangeFirstInsideInterval( + old_idx_min, old_idx_max, new_min, old_max + 1); + index_->SetMin(new_idx_min); + if (new_idx_min < old_idx_max) { + const int64 new_idx_max = callback_->RangeLastInsideInterval( + new_idx_min, old_idx_max, new_min, old_max + 1); + index_->SetMax(new_idx_max); + } + } + } + } + int64 Max() const override { + // Converting [index_->Min(), index_->Max()] to [idx_min, idx_max). + const int idx_min = index_->Min(); + const int idx_max = index_->Max() + 1; + return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max) + : kint64min; + } + void SetMax(int64 new_max) override { + const int64 old_min = Min(); + const int64 old_max = Max(); + if (old_min <= new_max && new_max < old_max) { + const int64 old_idx_min = index_->Min(); + const int64 old_idx_max = index_->Max() + 1; + if (old_idx_min < old_idx_max) { + const int64 new_idx_min = callback_->RangeFirstInsideInterval( + old_idx_min, old_idx_max, old_min, new_max + 1); + index_->SetMin(new_idx_min); + if (new_idx_min < old_idx_max) { + const int64 new_idx_max = callback_->RangeLastInsideInterval( + new_idx_min, old_idx_max, old_min, new_max + 1); + index_->SetMax(new_idx_max); + } + } + } + } + void WhenRange(Demon* d) override { index_->WhenRange(d); } + + private: + const RangeIntToIntFunction* const callback_; + IntVar* const index_; +}; + +IntExpr* MakeRangeMakeElementExpr(const RangeIntToIntFunction* callback, + IntVar* index, Solver* s) { + return s->RegisterIntExpr( + s->RevAlloc(new RangeMakeElementExpr(callback, index, s))); +} +} // namespace + +bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal( + const std::vector& pure_transits, + const std::vector& dependent_transits, + const RoutingDimension* base_dimension, int64 slack_max, int64 capacity, + VehicleEvaluator* vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name) { + RoutingDimension* new_dimension = nullptr; + if (base_dimension == nullptr) { + new_dimension = + new RoutingDimension(this, name, RoutingDimension::SelfBased()); + } else { + new_dimension = new RoutingDimension(this, name, base_dimension); + } + return InitializeDimensionInternal(pure_transits, dependent_transits, + slack_max, capacity, vehicle_capacity, + fix_start_cumul_to_zero, new_dimension); +} + +bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( + const std::vector& dependent_evaluators, + const RoutingDimension* base_dimension, int64 slack_max, + VehicleEvaluator* vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name) { + NodeEvaluator2* const zero_evaluator = + NewPermanentCallback(&ReturnZero); + const std::vector pure_transits(vehicles_, zero_evaluator); + return AddDimensionDependentDimensionWithVehicleCapacity( + pure_transits, dependent_evaluators, base_dimension, slack_max, + vehicle_capacity, fix_start_cumul_to_zero, name); +} + +bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( + NodeEvaluator2* pure_transits, VariableNodeEvaluator2* dependent_transits, + const RoutingDimension* base_dimension, int64 slack_max, + int64 vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& name) { + std::vector pure_evaluators(vehicles_, pure_transits); + std::vector transit_evaluators(vehicles_, + dependent_transits); + return AddDimensionDependentDimensionWithVehicleCapacityInternal( + pure_evaluators, transit_evaluators, base_dimension, slack_max, + vehicle_capacity, nullptr, fix_start_cumul_to_zero, name); +} + +bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( + VariableNodeEvaluator2* transits, const RoutingDimension* dimension, + int64 slack_max, int64 vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name) { + NodeEvaluator2* const zero_evaluator = + NewPermanentCallback(&ReturnZero); + return AddDimensionDependentDimensionWithVehicleCapacity( + zero_evaluator, transits, dimension, slack_max, vehicle_capacity, + fix_start_cumul_to_zero, name); +} + +RoutingModel::StateDependentTransit RoutingModel::MakeStateDependentTransit( + const std::function& f, int64 domain_start, + int64 domain_end) { + const std::function g = [&f](int64 x) { return f(x) + x; }; + // The next line is safe, because MakeCachedIntToIntFunction does not count + // on keeping the closure of its first argument alive. + return {MakeCachedIntToIntFunction(f, domain_start, domain_end), + MakeCachedRangeMinMaxIndexFunction(g, domain_start, domain_end)}; +} + +std::vector RoutingModel::GetAllDimensionNames() const { + std::vector dimension_names; + for (const auto& dimension_name_index : dimension_name_to_index_) { + dimension_names.push_back(dimension_name_index.first); + } + return dimension_names; } bool RoutingModel::HasDimension(const std::string& dimension_name) const { @@ -1065,7 +1502,7 @@ void RoutingModel::SetArcCostEvaluatorOfVehicle(NodeEvaluator2* evaluator, int vehicle) { CHECK(evaluator != nullptr); CHECK_LT(vehicle, vehicles_); - evaluator->CheckIsRepeatable(); + CHECK(evaluator->IsRepeatable()); transit_cost_of_vehicle_[vehicle] = evaluator; owned_node_callbacks_.insert(evaluator); } @@ -1088,11 +1525,6 @@ void RoutingModel::SetFixedCostOfVehicle(int64 cost, int vehicle) { } namespace { -template -static int64 ReturnZero(A a, B b) { - return 0; -} - // Some C++ versions used in the open-source export don't support comparison // functors for STL containers; so we need a comparator class instead. struct CostClassComparator { @@ -1115,15 +1547,14 @@ const RoutingModel::CostClassIndex RoutingModel::kCostClassIndexOfZeroCost = CostClassIndex(0); uint64 RoutingModel::GetFingerprintOfEvaluator( - RoutingModel::NodeEvaluator2* evaluator) const { - if (!FLAGS_routing_fingerprint_arc_cost_evaluators) { + RoutingModel::NodeEvaluator2* evaluator, + bool fingerprint_arc_cost_evaluators) const { + if (!fingerprint_arc_cost_evaluators) { // If we don't fingerprint the data returned by the evaluator, we can // just return the address as fingerprint (ensures that evaluators with the // same address are considered as equivalent). return bit_cast(evaluator); } - // Fingerprinting by matrix row seems to be the good combination to - // make ThoroughHash run fast and avoid using too much memory. uint64 evaluator_fprint = 0; const int max_row_size = Size() + vehicles_; std::unique_ptr row(new int64[max_row_size]); @@ -1150,7 +1581,8 @@ uint64 RoutingModel::GetFingerprintOfEvaluator( return evaluator_fprint; } -void RoutingModel::ComputeCostClasses() { +void RoutingModel::ComputeCostClasses( + const RoutingSearchParameters& parameters) { // First, detect if all non-null transit cost evaluators are equal. bool all_evaluators_equal = true; // Find first non-null evaluator. @@ -1209,9 +1641,12 @@ 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, + parameters.fingerprint_arc_cost_evaluators()); evaluator_to_fprint[uncached_evaluator] = evaluator_fprint; } NodeEvaluator2** cached_evaluator = @@ -1523,12 +1958,13 @@ void RoutingModel::SetStartEnd( // TODO(user): Remove the need for the homogeneous version once the // vehicle var to cost class element constraint is fast enough. -void RoutingModel::AppendHomogeneousArcCosts(int node_index, - std::vector* cost_elements) { +void RoutingModel::AppendHomogeneousArcCosts( + const RoutingSearchParameters& parameters, 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()) { + if (UsesLightPropagation(parameters)) { // 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). @@ -1546,11 +1982,12 @@ void RoutingModel::AppendHomogeneousArcCosts(int node_index, } } -void RoutingModel::AppendArcCosts(int node_index, +void RoutingModel::AppendArcCosts(const RoutingSearchParameters& parameters, + int node_index, std::vector* cost_elements) { CHECK(cost_elements != nullptr); DCHECK_GT(vehicles_, 0); - if (UsesLightPropagation()) { + if (UsesLightPropagation(parameters)) { // 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). @@ -1565,9 +2002,12 @@ void RoutingModel::AppendArcCosts(int node_index, cost_elements->push_back(var); } else { IntVar* const vehicle_class_var = - solver_->MakeElement([this](int64 index) { - return SafeGetCostClassInt64OfVehicle(index); - }, vehicle_vars_[node_index])->Var(); + solver_->MakeElement( + [this](int64 index) { + return SafeGetCostClassInt64OfVehicle(index); + }, + vehicle_vars_[node_index]) + ->Var(); IntExpr* const expr = solver_->MakeElement( [this, node_index](int64 next, int64 vehicle_class) { return GetArcCostForClass(node_index, next, vehicle_class); @@ -1586,16 +2026,18 @@ int RoutingModel::GetVehicleStartClass(int64 start_index) const { return kUnassigned; } -bool RoutingModel::ValidateSearchParameters() { +bool RoutingModel::ValidateSearchParameters( + const RoutingSearchParameters& search_parameters) { bool valid = true; - const RoutingStrategy first_solution_strategy = - GetSelectedFirstSolutionStrategy(); - if (GetFirstSolutionDecisionBuilder() == nullptr) { + const FirstSolutionStrategy::Value first_solution_strategy = + search_parameters.first_solution_strategy(); + if (GetFirstSolutionDecisionBuilder(search_parameters) == nullptr) { LOG(ERROR) << "Undefined first solution strategy: " << first_solution_strategy; valid = false; } - if (first_solution_strategy == ROUTING_SWEEP && sweep_arranger() == nullptr) { + if (first_solution_strategy == FirstSolutionStrategy::SWEEP && + sweep_arranger() == nullptr) { LOG(ERROR) << "Undefined sweep arranger for ROUTING_SWEEP strategy."; valid = false; } @@ -1606,7 +2048,16 @@ bool RoutingModel::ValidateSearchParameters() { return true; } +void RoutingModel::QuietCloseModel() { + QuietCloseModelWithParameters(DefaultSearchParameters()); +} + void RoutingModel::CloseModel() { + CloseModelWithParameters(DefaultSearchParameters()); +} + +void RoutingModel::CloseModelWithParameters( + const RoutingSearchParameters& parameters) { if (closed_) { LOG(WARNING) << "Model already closed"; return; @@ -1615,7 +2066,10 @@ void RoutingModel::CloseModel() { CheckDepot(); - ComputeCostClasses(); + for (RoutingDimension* const dimension : dimensions_) { + dimension->CloseModel(UsesLightPropagation(parameters)); + } + ComputeCostClasses(parameters); ComputeVehicleClasses(); vehicle_start_class_callback_.reset( NewPermanentCallback(this, &RoutingModel::GetVehicleStartClass)); @@ -1675,9 +2129,9 @@ void RoutingModel::CloseModel() { if (GetNonZeroCostClassesCount() > 0) { for (int node_index = 0; node_index < size; ++node_index) { if (CostsAreHomogeneousAcrossVehicles()) { - AppendHomogeneousArcCosts(node_index, &cost_elements); + AppendHomogeneousArcCosts(parameters, node_index, &cost_elements); } else { - AppendArcCosts(node_index, &cost_elements); + AppendArcCosts(parameters, node_index, &cost_elements); } } } @@ -1685,7 +2139,7 @@ void RoutingModel::CloseModel() { // Dimension span costs for (const RoutingDimension* dimension : dimensions_) { dimension->SetupGlobalSpanCost(&cost_elements); - dimension->SetupSlackCosts(&cost_elements); + dimension->SetupSlackAndDependentTransitCosts(&cost_elements); } // Penalty costs for (DisjunctionIndex i(0); i < disjunctions_.size(); ++i) { @@ -1710,11 +2164,11 @@ void RoutingModel::CloseModel() { // This will allow calling SetupSearch multiple times with different search // parameters. CreateNeighborhoodOperators(); - CreateFirstSolutionDecisionBuilders(); - if (!ValidateSearchParameters()) { + CreateFirstSolutionDecisionBuilders(parameters); + if (!ValidateSearchParameters(parameters)) { return; } - SetupSearch(); + SetupSearch(parameters); } struct Link { @@ -1794,8 +2248,7 @@ class RouteConstructor { node_to_chain_index_(nodes_number, -1), node_to_vehicle_class_index_(nodes_number, -1) { { - std::vector dimension_names; - model_->GetAllDimensions(&dimension_names); + const std::vector dimension_names = model_->GetAllDimensionNames(); dimensions_.assign(dimension_names.size(), nullptr); for (int i = 0; i < dimension_names.size(); ++i) { dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]); @@ -2514,7 +2967,7 @@ class FastOnePathBuilder : public DecisionBuilder { FastOnePathBuilder(RoutingModel* const model, ResultCallback2* evaluator) : model_(model), evaluator_(evaluator) { - evaluator_->CheckIsRepeatable(); + CHECK(evaluator_->IsRepeatable()); } ~FastOnePathBuilder() override {} Decision* Next(Solver* const solver) override { @@ -2534,8 +2987,8 @@ class FastOnePathBuilder : public DecisionBuilder { added_[index] = true; container->FastAdd(nexts[index])->SetValue(next); index = next; - std::vector alternates; - model_->GetDisjunctionIndicesFromIndex(index, &alternates); + const std::vector alternates = + model_->GetDisjunctionIndicesFromIndex(index); for (const int alternate : alternates) { if (index != alternate) { added_[alternate] = true; @@ -2653,72 +3106,33 @@ class AllUnperformed : public DecisionBuilder { RoutingModel* const model_; }; -// Flags override strategy selection -RoutingModel::RoutingStrategy RoutingModel::GetSelectedFirstSolutionStrategy() - const { - RoutingStrategy strategy; - if (ParseRoutingStrategy(FLAGS_routing_first_solution, &strategy)) { - return strategy; - } - return first_solution_strategy_; -} - -RoutingModel::RoutingMetaheuristic RoutingModel::GetSelectedMetaheuristic() - const { - if (FLAGS_routing_tabu_search) { - return ROUTING_TABU_SEARCH; - } else if (FLAGS_routing_simulated_annealing) { - return ROUTING_SIMULATED_ANNEALING; - } else if (FLAGS_routing_guided_local_search) { - return ROUTING_GUIDED_LOCAL_SEARCH; - } - return metaheuristic_; -} - void RoutingModel::AddSearchMonitor(SearchMonitor* const monitor) { monitors_.push_back(monitor); } -const Assignment* RoutingModel::SolveWithParameters( - const RoutingSearchParameters& p, const Assignment* assignment) { - FLAGS_routing_no_lns = p.no_lns; - FLAGS_routing_no_fullpathlns = p.no_fullpathlns; - FLAGS_routing_no_relocate = p.no_relocate; - FLAGS_routing_no_relocate_neighbors = p.no_relocate_neighbors; - FLAGS_routing_no_exchange = p.no_exchange; - FLAGS_routing_no_cross = p.no_cross; - FLAGS_routing_no_2opt = p.no_2opt; - FLAGS_routing_no_oropt = p.no_oropt; - FLAGS_routing_no_make_active = p.no_make_active; - FLAGS_routing_no_lkh = p.no_lkh; - FLAGS_routing_no_tsp = p.no_tsp; - FLAGS_routing_no_tsplns = p.no_tsplns; - FLAGS_routing_use_chain_make_inactive = p.use_chain_make_inactive; - FLAGS_routing_use_extended_swap_active = p.use_extended_swap_active; - FLAGS_routing_solution_limit = p.solution_limit; - FLAGS_routing_time_limit = p.time_limit; - time_limit_ms_ = p.time_limit; - FLAGS_routing_lns_time_limit = p.lns_time_limit; - lns_time_limit_ms_ = p.lns_time_limit; - FLAGS_routing_guided_local_search = p.guided_local_search; - FLAGS_routing_guided_local_search_lambda_coefficient = - p.guided_local_search_lambda_coefficient; - FLAGS_routing_simulated_annealing = p.simulated_annealing; - FLAGS_routing_tabu_search = p.tabu_search; - FLAGS_routing_dfs = p.dfs; - FLAGS_routing_first_solution = p.first_solution; - FLAGS_routing_use_first_solution_dive = p.use_first_solution_dive; - FLAGS_routing_optimization_step = p.optimization_step; - FLAGS_routing_trace = p.trace; - - return Solve(assignment); +const Assignment* RoutingModel::Solve(const Assignment* assignment) { + return SolveFromAssignmentWithParameters(assignment, + DefaultSearchParameters()); } -const Assignment* RoutingModel::Solve(const Assignment* assignment) { - QuietCloseModel(); +const Assignment* RoutingModel::SolveWithParameters( + const RoutingSearchParameters& parameters) { + return SolveFromAssignmentWithParameters(nullptr, parameters); +} + +const Assignment* RoutingModel::SolveFromAssignmentWithParameters( + const Assignment* assignment, const RoutingSearchParameters& parameters) { + QuietCloseModelWithParameters(parameters); + VLOG(1) << "Search parameters:\n" << parameters.DebugString(); if (status_ == ROUTING_INVALID) { return nullptr; } + solver_->UpdateLimits(parameters.time_limit_ms(), kint64max, kint64max, + parameters.solution_limit(), limit_); + solver_->UpdateLimits(parameters.time_limit_ms(), kint64max, kint64max, 1, + ls_limit_); + solver_->UpdateLimits(parameters.lns_time_limit_ms(), kint64max, kint64max, + kint64max, lns_limit_); const int64 start_time_ms = solver_->wall_time(); if (nullptr == assignment) { solver_->Solve(solve_db_, monitors_); @@ -2731,7 +3145,7 @@ const Assignment* RoutingModel::Solve(const Assignment* assignment) { status_ = ROUTING_SUCCESS; return collect_assignments_->solution(0); } else { - if (elapsed_time_ms >= time_limit_ms_) { + if (elapsed_time_ms >= parameters.time_limit_ms()) { status_ = ROUTING_FAIL_TIMEOUT; } else { status_ = ROUTING_FAIL; @@ -2896,13 +3310,24 @@ bool RoutingModel::ReplaceUnusedVehicle( Assignment* RoutingModel::CompactAssignment( const Assignment& assignment) const { + return CompactAssignmentInternal(assignment, false); +} + +Assignment* RoutingModel::CompactAndCheckAssignment( + const Assignment& assignment) const { + return CompactAssignmentInternal(assignment, true); +} + +Assignment* RoutingModel::CompactAssignmentInternal( + const Assignment& assignment, bool check_compact_assignment) const { CHECK_EQ(assignment.solver(), solver_.get()); if (!CostsAreHomogeneousAcrossVehicles()) { - LOG(INFO) << "The costs are not homogeneous, routes cannot be rearranged"; + LOG(WARNING) + << "The costs are not homogeneous, routes cannot be rearranged"; return nullptr; } - Assignment* compact_assignment = new Assignment(&assignment); + std::unique_ptr compact_assignment(new Assignment(&assignment)); for (int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) { if (IsVehicleUsed(*compact_assignment, vehicle)) { continue; @@ -2941,27 +3366,24 @@ Assignment* RoutingModel::CompactAssignment( // TODO(user): clarify the expected trigger rate of this LOG. LOG(INFO) << "No vehicle that can be swapped with " << vehicle << " was found"; - delete compact_assignment; return nullptr; } else { break; } } else { - if (!ReplaceUnusedVehicle(vehicle, swap_vehicle, compact_assignment)) { - delete compact_assignment; + if (!ReplaceUnusedVehicle(vehicle, swap_vehicle, + compact_assignment.get())) { return nullptr; } } } - if (FLAGS_routing_check_compact_assignment) { - if (!solver_->CheckAssignment(compact_assignment)) { - // TODO(user): clarify the expected trigger rate of this LOG. - LOG(INFO) << "The compacted assignment is not a valid solution"; - delete compact_assignment; - return nullptr; - } + if (check_compact_assignment && + !solver_->CheckAssignment(compact_assignment.get())) { + // TODO(user): clarify the expected trigger rate of this LOG. + LOG(WARNING) << "The compacted assignment is not a valid solution"; + return nullptr; } - return compact_assignment; + return compact_assignment.release(); } int RoutingModel::FindNextActive(int index, const std::vector& nodes) const { @@ -3001,118 +3423,22 @@ bool RoutingModel::ApplyLocksToAllVehicles( return RoutesToAssignment(locks, true, close_routes, preassignment_); } -void RoutingModel::UpdateTimeLimit(int64 limit_ms) { - time_limit_ms_ = limit_ms; - if (limit_ != nullptr) { - solver_->UpdateLimits(time_limit_ms_, kint64max, kint64max, - FLAGS_routing_solution_limit, limit_); - } - if (ls_limit_ != nullptr) { - solver_->UpdateLimits(time_limit_ms_, kint64max, kint64max, 1, ls_limit_); - } -} - -void RoutingModel::UpdateLNSTimeLimit(int64 limit_ms) { - lns_time_limit_ms_ = limit_ms; - if (lns_limit_ != nullptr) { - solver_->UpdateLimits(lns_time_limit_ms_, kint64max, kint64max, kint64max, - lns_limit_); - } -} - -int64 RoutingModel::GetNumberOfDecisionsInFirstSolution() const { +int64 RoutingModel::GetNumberOfDecisionsInFirstSolution( + const RoutingSearchParameters& parameters) const { IntVarFilteredDecisionBuilder* const decision_builder = - GetFilteredFirstSolutionDecisionBuilderOrNull(); + GetFilteredFirstSolutionDecisionBuilderOrNull(parameters); return decision_builder != nullptr ? decision_builder->number_of_decisions() : 0; } -int64 RoutingModel::GetNumberofRejectsInFirstSolution() const { +int64 RoutingModel::GetNumberOfRejectsInFirstSolution( + const RoutingSearchParameters& parameters) const { IntVarFilteredDecisionBuilder* const decision_builder = - GetFilteredFirstSolutionDecisionBuilderOrNull(); + GetFilteredFirstSolutionDecisionBuilderOrNull(parameters); return decision_builder != nullptr ? decision_builder->number_of_rejects() : 0; } -// static -const char* RoutingModel::RoutingStrategyName(RoutingStrategy strategy) { - switch (strategy) { - case ROUTING_DEFAULT_STRATEGY: - return "DefaultStrategy"; - case ROUTING_GLOBAL_CHEAPEST_ARC: - return "GlobalCheapestArc"; - case ROUTING_LOCAL_CHEAPEST_ARC: - return "LocalCheapestArc"; - case ROUTING_PATH_CHEAPEST_ARC: - return "PathCheapestArc"; - case ROUTING_PATH_MOST_CONSTRAINED_ARC: - return "PathMostConstrainedArc"; - case ROUTING_EVALUATOR_STRATEGY: - return "EvaluatorStrategy"; - case ROUTING_ALL_UNPERFORMED: - return "AllUnperformed"; - case ROUTING_BEST_INSERTION: - return "BestInsertion"; - case ROUTING_GLOBAL_CHEAPEST_INSERTION: - return "GlobalCheapestInsertion"; - case ROUTING_LOCAL_CHEAPEST_INSERTION: - return "LocalCheapestInsertion"; - case ROUTING_SAVINGS: - return "Savings"; - case ROUTING_SWEEP: - return "Sweep"; - default: - return nullptr; - } - return nullptr; -} - -// static -bool RoutingModel::ParseRoutingStrategy(const std::string& strategy_str, - RoutingStrategy* strategy) { - for (int i = 0;; ++i) { - const RoutingStrategy cur_strategy = static_cast(i); - const char* cur_name = RoutingStrategyName(cur_strategy); - if (cur_name == nullptr) return false; - if (strategy_str == cur_name) { - *strategy = cur_strategy; - return true; - } - } - return false; // Visual Studio complains without it. -} - -// static -const char* RoutingModel::RoutingMetaheuristicName( - RoutingMetaheuristic metaheuristic) { - switch (metaheuristic) { - case ROUTING_GREEDY_DESCENT: - return "GreedyDescent"; - case ROUTING_GUIDED_LOCAL_SEARCH: - return "GuidedLocalSearch"; - case ROUTING_SIMULATED_ANNEALING: - return "SimulatedAnnealing"; - case ROUTING_TABU_SEARCH: - return "TabuSearch"; - } - return nullptr; -} - -// static -bool RoutingModel::ParseRoutingMetaheuristic( - const std::string& metaheuristic_str, RoutingMetaheuristic* metaheuristic) { - for (int i = 0;; ++i) { - const RoutingMetaheuristic cur_metaheuristic = - static_cast(i); - const char* cur_name = RoutingMetaheuristicName(cur_metaheuristic); - if (cur_name == nullptr) return false; - if (metaheuristic_str == cur_name) { - *metaheuristic = cur_metaheuristic; - return true; - } - } -} - bool RoutingModel::WriteAssignment(const std::string& file_name) const { if (collect_assignments_->solution_count() == 1 && assignment_ != nullptr) { assignment_->Copy(collect_assignments_->solution(0)); @@ -3569,8 +3895,7 @@ std::string RoutingModel::DebugOutputAssignment( std::string output; hash_set dimension_names; if (dimension_to_print == "") { - std::vector all_dimension_names; - GetAllDimensions(&all_dimension_names); + const std::vector all_dimension_names = GetAllDimensionNames(); dimension_names.insert(all_dimension_names.begin(), all_dimension_names.end()); } else { @@ -3647,50 +3972,51 @@ Assignment* RoutingModel::GetOrCreateAssignment() { SearchLimit* RoutingModel::GetOrCreateLimit() { if (limit_ == nullptr) { - limit_ = solver_->MakeLimit(time_limit_ms_, kint64max, kint64max, - FLAGS_routing_solution_limit, true); + limit_ = + solver_->MakeLimit(kint64max, kint64max, kint64max, kint64max, true); } return limit_; } SearchLimit* RoutingModel::GetOrCreateLocalSearchLimit() { if (ls_limit_ == nullptr) { - ls_limit_ = - solver_->MakeLimit(time_limit_ms_, kint64max, kint64max, 1, true); + ls_limit_ = solver_->MakeLimit(kint64max, kint64max, kint64max, 1, true); } return ls_limit_; } SearchLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() { if (lns_limit_ == nullptr) { - lns_limit_ = - solver_->MakeLimit(lns_time_limit_ms_, kint64max, kint64max, kint64max); + lns_limit_ = solver_->MakeLimit(kint64max, kint64max, kint64max, kint64max); } return lns_limit_; } LocalSearchOperator* RoutingModel::CreateInsertionOperator() { std::vector empty; - if (pickup_delivery_pairs_.size() > 0) { - return MakePairActive( - solver_.get(), nexts_, - CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, - vehicle_start_class_callback_.get(), pickup_delivery_pairs_); - } else { - return MakeLocalSearchOperator( - solver_.get(), nexts_, - CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, - vehicle_start_class_callback_.get()); + LocalSearchOperator* insertion_operator = + MakeLocalSearchOperator( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_.get()); + if (!pickup_delivery_pairs_.empty()) { + insertion_operator = solver_->ConcatenateOperators( + {MakePairActive( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_.get(), pickup_delivery_pairs_), + insertion_operator}); } + return insertion_operator; } -#define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type) \ - if (CostsAreHomogeneousAcrossVehicles()) { \ - local_search_operators_[operator_type] = \ - solver_->MakeOperator(nexts_, cp_operator_type); \ - } else { \ - local_search_operators_[operator_type] = \ - solver_->MakeOperator(nexts_, vehicle_vars_, cp_operator_type); \ +#define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type) \ + if (CostsAreHomogeneousAcrossVehicles()) { \ + local_search_operators_[operator_type] = \ + solver_->MakeOperator(nexts_, Solver::cp_operator_type); \ + } else { \ + local_search_operators_[operator_type] = solver_->MakeOperator( \ + nexts_, vehicle_vars_, Solver::cp_operator_type); \ } #define CP_ROUTING_ADD_OPERATOR2(operator_type, cp_operator_class) \ @@ -3701,126 +4027,126 @@ LocalSearchOperator* RoutingModel::CreateInsertionOperator() { : vehicle_vars_, \ vehicle_start_class_callback_.get()); -#define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \ - if (CostsAreHomogeneousAcrossVehicles()) { \ - 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_, [this](int64 i, int64 j, int64 k) { \ - return GetArcCostForVehicle(i, j, k); \ - }, cp_operator_type); \ +#define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \ + if (CostsAreHomogeneousAcrossVehicles()) { \ + local_search_operators_[operator_type] = solver_->MakeOperator( \ + nexts_, [this](int64 i, int64 j, \ + int64 k) { return GetArcCostForVehicle(i, j, k); }, \ + Solver::cp_operator_type); \ + } else { \ + local_search_operators_[operator_type] = \ + solver_->MakeOperator(nexts_, vehicle_vars_, \ + [this](int64 i, int64 j, int64 k) { \ + return GetArcCostForVehicle(i, j, k); \ + }, \ + Solver::cp_operator_type); \ } void RoutingModel::CreateNeighborhoodOperators() { local_search_operators_.clear(); - local_search_operators_.resize(ROUTING_LOCAL_SEARCH_OPERATOR_COUNTER, - nullptr); - CP_ROUTING_ADD_OPERATOR2(ROUTING_RELOCATE, Relocate); + local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER, nullptr); + CP_ROUTING_ADD_OPERATOR2(RELOCATE, Relocate); std::vector empty; - local_search_operators_[ROUTING_PAIR_RELOCATE] = MakePairRelocate( + local_search_operators_[RELOCATE_PAIR] = MakePairRelocate( solver_.get(), nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, vehicle_start_class_callback_.get(), pickup_delivery_pairs_); - local_search_operators_[ROUTING_RELOCATE_NEIGHBORS] = MakeRelocateNeighbors( + local_search_operators_[RELOCATE_NEIGHBORS] = MakeRelocateNeighbors( solver_.get(), nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, vehicle_start_class_callback_.get(), NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost)); - CP_ROUTING_ADD_OPERATOR2(ROUTING_EXCHANGE, Exchange); - CP_ROUTING_ADD_OPERATOR2(ROUTING_CROSS, Cross); - CP_ROUTING_ADD_OPERATOR2(ROUTING_TWO_OPT, TwoOpt); - CP_ROUTING_ADD_OPERATOR(ROUTING_OR_OPT, Solver::OROPT); - CP_ROUTING_ADD_CALLBACK_OPERATOR(ROUTING_LKH, Solver::LK); - local_search_operators_[ROUTING_MAKE_ACTIVE] = CreateInsertionOperator(); - CP_ROUTING_ADD_OPERATOR2(ROUTING_MAKE_INACTIVE, MakeInactiveOperator); - CP_ROUTING_ADD_OPERATOR2(ROUTING_MAKE_CHAIN_INACTIVE, - MakeChainInactiveOperator); - CP_ROUTING_ADD_OPERATOR2(ROUTING_SWAP_ACTIVE, SwapActiveOperator); - CP_ROUTING_ADD_OPERATOR2(ROUTING_EXTENDED_SWAP_ACTIVE, - ExtendedSwapActiveOperator); - CP_ROUTING_ADD_CALLBACK_OPERATOR(ROUTING_TSP_OPT, Solver::TSPOPT); - CP_ROUTING_ADD_CALLBACK_OPERATOR(ROUTING_TSP_LNS, Solver::TSPLNS); - CP_ROUTING_ADD_OPERATOR(ROUTING_PATH_LNS, Solver::PATHLNS); - CP_ROUTING_ADD_OPERATOR(ROUTING_FULL_PATH_LNS, Solver::FULLPATHLNS); - CP_ROUTING_ADD_OPERATOR(ROUTING_INACTIVE_LNS, Solver::UNACTIVELNS); + local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators( + {NodePairSwapActive( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_.get(), pickup_delivery_pairs_), + PairNodeSwapActive( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_.get(), pickup_delivery_pairs_)}); + CP_ROUTING_ADD_OPERATOR2(EXCHANGE, Exchange); + CP_ROUTING_ADD_OPERATOR2(CROSS, Cross); + CP_ROUTING_ADD_OPERATOR2(TWO_OPT, TwoOpt); + CP_ROUTING_ADD_OPERATOR(OR_OPT, OROPT); + CP_ROUTING_ADD_CALLBACK_OPERATOR(LIN_KERNIGHAN, LK); + local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator(); + CP_ROUTING_ADD_OPERATOR2(MAKE_INACTIVE, MakeInactiveOperator); + CP_ROUTING_ADD_OPERATOR2(MAKE_CHAIN_INACTIVE, MakeChainInactiveOperator); + CP_ROUTING_ADD_OPERATOR2(SWAP_ACTIVE, SwapActiveOperator); + CP_ROUTING_ADD_OPERATOR2(EXTENDED_SWAP_ACTIVE, ExtendedSwapActiveOperator); + CP_ROUTING_ADD_CALLBACK_OPERATOR(TSP_OPT, TSPOPT); + CP_ROUTING_ADD_CALLBACK_OPERATOR(TSP_LNS, TSPLNS); + CP_ROUTING_ADD_OPERATOR(PATH_LNS, PATHLNS); + CP_ROUTING_ADD_OPERATOR(FULL_PATH_LNS, FULLPATHLNS); + CP_ROUTING_ADD_OPERATOR(INACTIVE_LNS, UNACTIVELNS); } #undef CP_ROUTING_ADD_CALLBACK_OPERATOR +#undef CP_ROUTING_ADD_OPERATOR2 #undef CP_ROUTING_ADD_OPERATOR -LocalSearchOperator* RoutingModel::GetNeighborhoodOperators() const { +#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \ + if (search_parameters.local_search_operators().use_##operator_method()) { \ + operators.push_back(local_search_operators_[operator_type]); \ + } + +LocalSearchOperator* RoutingModel::GetNeighborhoodOperators( + const RoutingSearchParameters& search_parameters) const { std::vector operators = extra_operators_; if (pickup_delivery_pairs_.size() > 0) { - operators.push_back(local_search_operators_[ROUTING_PAIR_RELOCATE]); + CP_ROUTING_PUSH_OPERATOR(RELOCATE_PAIR, relocate_pair, operators); + CP_ROUTING_PUSH_OPERATOR(NODE_PAIR_SWAP, node_pair_swap_active, operators); } if (vehicles_ > 1) { - if (!FLAGS_routing_no_relocate) { - operators.push_back(local_search_operators_[ROUTING_RELOCATE]); - } - if (!FLAGS_routing_no_exchange) { - operators.push_back(local_search_operators_[ROUTING_EXCHANGE]); - } - if (!FLAGS_routing_no_cross) { - operators.push_back(local_search_operators_[ROUTING_CROSS]); - } + CP_ROUTING_PUSH_OPERATOR(RELOCATE, relocate, operators); + CP_ROUTING_PUSH_OPERATOR(EXCHANGE, exchange, operators); + CP_ROUTING_PUSH_OPERATOR(CROSS, cross, operators); } if (pickup_delivery_pairs_.size() > 0 || - !FLAGS_routing_no_relocate_neighbors) { - operators.push_back(local_search_operators_[ROUTING_RELOCATE_NEIGHBORS]); + search_parameters.local_search_operators().use_relocate_neighbors()) { + operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]); } - if (!FLAGS_routing_no_lkh && !FLAGS_routing_tabu_search && - !FLAGS_routing_simulated_annealing) { - operators.push_back(local_search_operators_[ROUTING_LKH]); + const LocalSearchMetaheuristic::Value local_search_metaheuristic = + search_parameters.local_search_metaheuristic(); + if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH && + local_search_metaheuristic != + LocalSearchMetaheuristic::SIMULATED_ANNEALING) { + CP_ROUTING_PUSH_OPERATOR(LIN_KERNIGHAN, lin_kernighan, operators); } - if (!FLAGS_routing_no_2opt) { - operators.push_back(local_search_operators_[ROUTING_TWO_OPT]); + CP_ROUTING_PUSH_OPERATOR(TWO_OPT, two_opt, operators); + CP_ROUTING_PUSH_OPERATOR(OR_OPT, or_opt, operators); + if (disjunctions_.size() != 0) { + CP_ROUTING_PUSH_OPERATOR(MAKE_INACTIVE, make_inactive, operators); + CP_ROUTING_PUSH_OPERATOR(MAKE_CHAIN_INACTIVE, make_chain_inactive, + operators); + CP_ROUTING_PUSH_OPERATOR(MAKE_ACTIVE, make_active, operators); + CP_ROUTING_PUSH_OPERATOR(SWAP_ACTIVE, swap_active, operators); + CP_ROUTING_PUSH_OPERATOR(EXTENDED_SWAP_ACTIVE, extended_swap_active, + operators); } - if (!FLAGS_routing_no_oropt) { - operators.push_back(local_search_operators_[ROUTING_OR_OPT]); + // TODO(user): move the following operators to a second local search + // loop. + if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH && + local_search_metaheuristic != + LocalSearchMetaheuristic::SIMULATED_ANNEALING) { + CP_ROUTING_PUSH_OPERATOR(TSP_OPT, tsp_opt, operators); } - if (!FLAGS_routing_no_make_active && disjunctions_.size() != 0) { - if (!FLAGS_routing_use_chain_make_inactive) { - operators.push_back(local_search_operators_[ROUTING_MAKE_INACTIVE]); - } else { - operators.push_back(local_search_operators_[ROUTING_MAKE_CHAIN_INACTIVE]); - } - - // TODO(user): On cases where we have a mix of node pairs and - // individual nodes, only pairs are going to be made active. In practice - // such cases should not appear, but we might want to be robust to them - // anyway. - operators.push_back(local_search_operators_[ROUTING_MAKE_ACTIVE]); - if (!FLAGS_routing_use_extended_swap_active) { - operators.push_back(local_search_operators_[ROUTING_SWAP_ACTIVE]); - } else { - operators.push_back( - local_search_operators_[ROUTING_EXTENDED_SWAP_ACTIVE]); - } + if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH && + local_search_metaheuristic != + LocalSearchMetaheuristic::SIMULATED_ANNEALING) { + CP_ROUTING_PUSH_OPERATOR(TSP_LNS, tsp_lns, operators); } - // TODO(user): move the following operators to a second local search loop. - if (!FLAGS_routing_no_tsp && !FLAGS_routing_tabu_search && - !FLAGS_routing_simulated_annealing) { - operators.push_back(local_search_operators_[ROUTING_TSP_OPT]); - } - if (!FLAGS_routing_no_tsplns && !FLAGS_routing_tabu_search && - !FLAGS_routing_simulated_annealing) { - operators.push_back(local_search_operators_[ROUTING_TSP_LNS]); - } - if (!FLAGS_routing_no_fullpathlns) { - operators.push_back(local_search_operators_[ROUTING_FULL_PATH_LNS]); - } - if (!FLAGS_routing_no_lns) { - operators.push_back(local_search_operators_[ROUTING_PATH_LNS]); - if (disjunctions_.size() != 0) { - operators.push_back(local_search_operators_[ROUTING_INACTIVE_LNS]); - } + CP_ROUTING_PUSH_OPERATOR(FULL_PATH_LNS, full_path_lns, operators); + CP_ROUTING_PUSH_OPERATOR(PATH_LNS, path_lns, operators); + if (disjunctions_.size() != 0) { + CP_ROUTING_PUSH_OPERATOR(INACTIVE_LNS, inactive_lns, operators); } return solver_->ConcatenateOperators(operators); } +#undef CP_ROUTING_PUSH_OPERATOR + const std::vector& RoutingModel::GetOrCreateLocalSearchFilters() { // Note on objective injection from one filter to another. @@ -3830,7 +4156,7 @@ RoutingModel::GetOrCreateLocalSearchFilters() { // - PathCumulFilter: takes dimension span costs into account, // - LocalSearchObjectiveFilter: takes dimension "arc" costs into account. // To be able to filter cost values properly, a filter needs to be aware of - // of cost bounds computed by other filters before it (for the same delta). + // cost bounds computed by other filters before it (for the same delta). // Communication of cost between filters is done through callbacks, // LocalSearchObjectiveFilter sending total arc costs to // NodeDisjunctionFilter, itself sending this cost + total penalty cost to @@ -3840,24 +4166,22 @@ RoutingModel::GetOrCreateLocalSearchFilters() { if (filters_.empty()) { std::vector path_cumul_filters; RoutingLocalSearchFilter* path_cumul_filter = nullptr; - if (FLAGS_routing_use_path_cumul_filter) { - for (const RoutingDimension* dimension : dimensions_) { - Solver::ObjectiveWatcher objective_callback = nullptr; - if (path_cumul_filter != nullptr) { - objective_callback = [path_cumul_filter](int64 value) { - return path_cumul_filter->InjectObjectiveValue(value); - }; - } - path_cumul_filter = - MakePathCumulFilter(*this, *dimension, objective_callback); - path_cumul_filters.push_back(path_cumul_filter); + for (const RoutingDimension* dimension : dimensions_) { + Solver::ObjectiveWatcher objective_callback = nullptr; + if (path_cumul_filter != nullptr) { + objective_callback = [path_cumul_filter](int64 value) { + return path_cumul_filter->InjectObjectiveValue(value); + }; } - // Due to the way cost injection is setup, path filters have to be - // called in reverse order. - std::reverse(path_cumul_filters.begin(), path_cumul_filters.end()); + path_cumul_filter = + MakePathCumulFilter(*this, *dimension, objective_callback); + path_cumul_filters.push_back(path_cumul_filter); } + // Due to the way cost injection is setup, path filters have to be + // called in reverse order. + std::reverse(path_cumul_filters.begin(), path_cumul_filters.end()); RoutingLocalSearchFilter* node_disjunction_filter = nullptr; - if (FLAGS_routing_use_disjunction_filter && !disjunctions_.empty()) { + if (!disjunctions_.empty()) { Solver::ObjectiveWatcher objective_callback = nullptr; if (path_cumul_filter != nullptr) { objective_callback = [path_cumul_filter](int64 value) { @@ -3867,49 +4191,43 @@ RoutingModel::GetOrCreateLocalSearchFilters() { node_disjunction_filter = MakeNodeDisjunctionFilter(*this, objective_callback); } - if (FLAGS_routing_use_objective_filter) { - Solver::ObjectiveWatcher objective_callback = nullptr; - if (node_disjunction_filter != nullptr) { - objective_callback = [node_disjunction_filter](int64 value) { - return node_disjunction_filter->InjectObjectiveValue(value); - }; - } else if (path_cumul_filter != nullptr) { - objective_callback = [path_cumul_filter](int64 value) { - return path_cumul_filter->InjectObjectiveValue(value); - }; - } - if (CostsAreHomogeneousAcrossVehicles()) { - LocalSearchFilter* filter = solver_->MakeLocalSearchObjectiveFilter( - 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_, [this](int64 i, int64 j, int64 k) { - return GetArcCostForVehicle(i, j, k); - }, objective_callback, cost_, Solver::LE, Solver::SUM); - filters_.push_back(filter); - } + Solver::ObjectiveWatcher objective_callback = nullptr; + if (node_disjunction_filter != nullptr) { + objective_callback = [node_disjunction_filter](int64 value) { + return node_disjunction_filter->InjectObjectiveValue(value); + }; + } else if (path_cumul_filter != nullptr) { + objective_callback = [path_cumul_filter](int64 value) { + return path_cumul_filter->InjectObjectiveValue(value); + }; + } + if (CostsAreHomogeneousAcrossVehicles()) { + LocalSearchFilter* filter = solver_->MakeLocalSearchObjectiveFilter( + 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_, + [this](int64 i, int64 j, int64 k) { + return GetArcCostForVehicle(i, j, k); + }, + objective_callback, cost_, Solver::LE, Solver::SUM); + filters_.push_back(filter); } filters_.push_back(solver_->MakeVariableDomainFilter()); if (node_disjunction_filter != nullptr) { // Must be added after ObjectiveFilter. filters_.push_back(node_disjunction_filter); } - if (FLAGS_routing_use_pickup_and_delivery_filter && - pickup_delivery_pairs_.size() > 0) { + if (pickup_delivery_pairs_.size() > 0) { filters_.push_back( MakeNodePrecedenceFilter(*this, pickup_delivery_pairs_)); } - if (FLAGS_routing_use_vehicle_var_filter) { - filters_.push_back(MakeVehicleVarFilter(*this)); - } - if (FLAGS_routing_use_path_cumul_filter) { - // Must be added after NodeDisjunctionFilter and ObjectiveFilter. - filters_.insert(filters_.end(), path_cumul_filters.begin(), - path_cumul_filters.end()); - } + filters_.push_back(MakeVehicleVarFilter(*this)); + // Must be added after NodeDisjunctionFilter and ObjectiveFilter. + filters_.insert(filters_.end(), path_cumul_filters.begin(), + path_cumul_filters.end()); filters_.insert(filters_.end(), extra_filters_.begin(), extra_filters_.end()); } @@ -3918,24 +4236,19 @@ RoutingModel::GetOrCreateLocalSearchFilters() { const std::vector& RoutingModel::GetOrCreateFeasibilityFilters() { if (feasibility_filters_.empty()) { - if (FLAGS_routing_use_path_cumul_filter) { - for (const RoutingDimension* const dimension : dimensions_) { - feasibility_filters_.push_back( - MakePathCumulFilter(*this, *dimension, nullptr)); - } + for (const RoutingDimension* const dimension : dimensions_) { + feasibility_filters_.push_back( + MakePathCumulFilter(*this, *dimension, nullptr)); } - if (FLAGS_routing_use_disjunction_filter && !disjunctions_.empty()) { + if (!disjunctions_.empty()) { feasibility_filters_.push_back(MakeNodeDisjunctionFilter(*this, nullptr)); } feasibility_filters_.push_back(solver_->MakeVariableDomainFilter()); - if (FLAGS_routing_use_pickup_and_delivery_filter && - pickup_delivery_pairs_.size() > 0) { + if (pickup_delivery_pairs_.size() > 0) { feasibility_filters_.push_back( MakeNodePrecedenceFilter(*this, pickup_delivery_pairs_)); } - if (FLAGS_routing_use_vehicle_var_filter) { - feasibility_filters_.push_back(MakeVehicleVarFilter(*this)); - } + feasibility_filters_.push_back(MakeVehicleVarFilter(*this)); feasibility_filters_.insert(feasibility_filters_.end(), extra_filters_.begin(), extra_filters_.end()); } @@ -3957,83 +4270,94 @@ DecisionBuilder* RoutingModel::CreateSolutionFinalizer() { return solver_->Compose(decision_builders); } -void RoutingModel::CreateFirstSolutionDecisionBuilders() { +void RoutingModel::CreateFirstSolutionDecisionBuilders( + const RoutingSearchParameters& search_parameters) { first_solution_decision_builders_.resize( - ROUTING_FIRST_SOLUTION_STRATEGY_COUNTER); + FirstSolutionStrategy_Value_Value_ARRAYSIZE, nullptr); first_solution_filtered_decision_builders_.resize( - ROUTING_FIRST_SOLUTION_STRATEGY_COUNTER, nullptr); + FirstSolutionStrategy_Value_Value_ARRAYSIZE, nullptr); DecisionBuilder* const finalize_solution = CreateSolutionFinalizer(); // Default heuristic - first_solution_decision_builders_[ROUTING_DEFAULT_STRATEGY] = - finalize_solution; + first_solution_decision_builders_ + [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution; // Global cheapest addition heuristic. - first_solution_decision_builders_[ROUTING_GLOBAL_CHEAPEST_ARC] = - solver_->MakePhase(nexts_, [this](int64 i, int64 j) { - return GetArcCostForFirstSolution(i, j); - }, Solver::CHOOSE_STATIC_GLOBAL_BEST); + first_solution_decision_builders_ + [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = 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] = + first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] = solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, - Solver::IndexEvaluator2([this](int64 i, int64 j) { + [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, - Solver::IndexEvaluator2([this](int64 i, int64 j) { - return GetArcCostForFirstSolution(i, j); - })); + first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] = + 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( this, NewPermanentCallback( this, &RoutingModel::GetArcCostForFirstSolution))); - first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC] = solver_->Try( - fast_one_path_builder, - first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC]); - } else if (FLAGS_routing_use_filtered_first_solutions) { - first_solution_filtered_decision_builders_[ROUTING_PATH_CHEAPEST_ARC] = - solver_->RevAlloc(new EvaluatorCheapestAdditionFilteredDecisionBuilder( - this, NewPermanentCallback( - this, &RoutingModel::GetArcCostForFirstSolution), - GetOrCreateFeasibilityFilters())); - first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC] = solver_->Try( - first_solution_filtered_decision_builders_[ROUTING_PATH_CHEAPEST_ARC], - first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC]); + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_CHEAPEST_ARC] = + solver_->Try(fast_one_path_builder, + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_CHEAPEST_ARC]); + } else if (search_parameters.use_filtered_first_solution_strategy()) { + first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::PATH_CHEAPEST_ARC] = solver_->RevAlloc( + new EvaluatorCheapestAdditionFilteredDecisionBuilder( + this, NewPermanentCallback( + this, &RoutingModel::GetArcCostForFirstSolution), + GetOrCreateFeasibilityFilters())); + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_CHEAPEST_ARC] = + solver_->Try(first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::PATH_CHEAPEST_ARC], + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_CHEAPEST_ARC]); } // Path-based most constrained arc addition heuristic. - first_solution_decision_builders_[ROUTING_PATH_MOST_CONSTRAINED_ARC] = - solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, - Solver::VariableValueComparator([this](int64 i, int64 j, int64 k) { - return ArcIsMoreConstrainedThanArc(i, j, k); - })); - if (FLAGS_routing_use_filtered_first_solutions) { + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->MakePhase( + nexts_, Solver::CHOOSE_PATH, [this](int64 i, int64 j, int64 k) { + return ArcIsMoreConstrainedThanArc(i, j, k); + }); + if (search_parameters.use_filtered_first_solution_strategy()) { first_solution_filtered_decision_builders_ - [ROUTING_PATH_MOST_CONSTRAINED_ARC] = solver_->RevAlloc( + [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->RevAlloc( new ComparatorCheapestAdditionFilteredDecisionBuilder( - this, [this](int64 i, int64 j, int64 k) { + 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], - first_solution_decision_builders_ - [ROUTING_PATH_MOST_CONSTRAINED_ARC]); + }, + GetOrCreateFeasibilityFilters())); + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try( + first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC], + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]); } // Evaluator-based path heuristic. if (first_solution_evaluator_ != nullptr) { - first_solution_decision_builders_[ROUTING_EVALUATOR_STRATEGY] = - solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, - first_solution_evaluator_); + first_solution_decision_builders_ + [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase( + nexts_, Solver::CHOOSE_PATH, first_solution_evaluator_); } else { - first_solution_decision_builders_[ROUTING_EVALUATOR_STRATEGY] = nullptr; + first_solution_decision_builders_ + [FirstSolutionStrategy::EVALUATOR_STRATEGY] = nullptr; } // All unperformed heuristic. - first_solution_decision_builders_[ROUTING_ALL_UNPERFORMED] = + first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] = solver_->RevAlloc(new AllUnperformed(this)); // Best insertion heuristic. - SearchLimit* const ls_limit = - solver_->MakeLimit(time_limit_ms_, kint64max, kint64max, kint64max, true); + SearchLimit* const ls_limit = solver_->MakeLimit( + search_parameters.time_limit_ms(), kint64max, kint64max, kint64max, true); DecisionBuilder* const finalize = solver_->MakeSolveOnce( finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit()); LocalSearchPhaseParameters* const insertion_parameters = @@ -4047,76 +4371,86 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders() { decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(), vehicle_vars_.end()); } - first_solution_decision_builders_[ROUTING_BEST_INSERTION] = + first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] = solver_->MakeNestedOptimize( solver_->MakeLocalSearchPhase( decision_vars, solver_->RevAlloc(new AllUnperformed(this)), insertion_parameters), - GetOrCreateAssignment(), false, FLAGS_routing_optimization_step, + GetOrCreateAssignment(), false, search_parameters.optimization_step(), monitors); - first_solution_decision_builders_[ROUTING_BEST_INSERTION] = solver_->Compose( - first_solution_decision_builders_[ROUTING_BEST_INSERTION], finalize); + first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] = + solver_->Compose(first_solution_decision_builders_ + [FirstSolutionStrategy::BEST_INSERTION], + finalize); // Global cheapest insertion first_solution_filtered_decision_builders_ - [ROUTING_GLOBAL_CHEAPEST_INSERTION] = + [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION] = solver_->RevAlloc(new GlobalCheapestInsertionFilteredDecisionBuilder( this, NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), NewPermanentCallback(this, &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]); + first_solution_decision_builders_ + [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION] = + solver_->Try(first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION], + first_solution_decision_builders_ + [FirstSolutionStrategy::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]); + first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = + solver_->RevAlloc(new LocalCheapestInsertionFilteredDecisionBuilder( + this, + NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), + GetOrCreateFeasibilityFilters())); + first_solution_decision_builders_ + [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = + solver_->Try(first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION], + first_solution_decision_builders_ + [FirstSolutionStrategy::BEST_INSERTION]); // Savings - if (FLAGS_routing_use_filtered_first_solutions) { - first_solution_filtered_decision_builders_[ROUTING_SAVINGS] = + if (search_parameters.use_filtered_first_solution_strategy()) { + first_solution_filtered_decision_builders_[FirstSolutionStrategy::SAVINGS] = solver_->RevAlloc(new SavingsFilteredDecisionBuilder( this, FLAGS_savings_filter_neighbors, GetOrCreateFeasibilityFilters())); - first_solution_decision_builders_[ROUTING_SAVINGS] = solver_->Try( - first_solution_filtered_decision_builders_[ROUTING_SAVINGS], - solver_->RevAlloc(new SavingsBuilder(this, true))); + first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] = + solver_->Try(first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::SAVINGS], + solver_->RevAlloc(new SavingsBuilder(this, true))); } else { - first_solution_decision_builders_[ROUTING_SAVINGS] = + first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] = solver_->RevAlloc(new SavingsBuilder(this, true)); DecisionBuilder* savings_builder = solver_->RevAlloc(new SavingsBuilder(this, false)); - first_solution_decision_builders_[ROUTING_SAVINGS] = solver_->Try( - savings_builder, first_solution_decision_builders_[ROUTING_SAVINGS]); + first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] = + solver_->Try( + savings_builder, + first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS]); } // Sweep - first_solution_decision_builders_[ROUTING_SWEEP] = + first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] = solver_->RevAlloc(new SweepBuilder(this, true)); DecisionBuilder* sweep_builder = solver_->RevAlloc(new SweepBuilder(this, false)); - first_solution_decision_builders_[ROUTING_SWEEP] = solver_->Try( - sweep_builder, first_solution_decision_builders_[ROUTING_SWEEP]); - if (FLAGS_routing_use_first_solution_dive) { - DecisionBuilder* 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]); - } - } + first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] = + solver_->Try( + sweep_builder, + first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]); + // Automatic + // TODO(user): make this smarter. + first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] = + first_solution_decision_builders_ + [FirstSolutionStrategy::PATH_CHEAPEST_ARC]; } -DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder() const { - const RoutingStrategy first_solution_strategy = - GetSelectedFirstSolutionStrategy(); +DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder( + const RoutingSearchParameters& search_parameters) const { + const FirstSolutionStrategy::Value first_solution_strategy = + search_parameters.first_solution_strategy(); if (first_solution_strategy < first_solution_decision_builders_.size()) { - VLOG(1) << "Using first solution strategy: " - << RoutingStrategyName(first_solution_strategy); return first_solution_decision_builders_[first_solution_strategy]; } else { return nullptr; @@ -4124,24 +4458,29 @@ DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder() const { } IntVarFilteredDecisionBuilder* -RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull() const { - const RoutingStrategy first_solution_strategy = - GetSelectedFirstSolutionStrategy(); +RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull( + const RoutingSearchParameters& search_parameters) const { + const FirstSolutionStrategy::Value first_solution_strategy = + search_parameters.first_solution_strategy(); return first_solution_filtered_decision_builders_[first_solution_strategy]; } -LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters() { +LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters( + const RoutingSearchParameters& search_parameters) { return solver_->MakeLocalSearchPhaseParameters( - GetNeighborhoodOperators(), + GetNeighborhoodOperators(search_parameters), solver_->MakeSolveOnce(CreateSolutionFinalizer(), GetOrCreateLargeNeighborhoodSearchLimit()), GetOrCreateLocalSearchLimit(), GetOrCreateLocalSearchFilters()); } -DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder() { +DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder( + const RoutingSearchParameters& search_parameters) { const int size = Size(); - DecisionBuilder* first_solution = GetFirstSolutionDecisionBuilder(); - LocalSearchPhaseParameters* parameters = CreateLocalSearchParameters(); + DecisionBuilder* first_solution = + GetFirstSolutionDecisionBuilder(search_parameters); + LocalSearchPhaseParameters* const parameters = + CreateLocalSearchParameters(search_parameters); if (CostsAreHomogeneousAcrossVehicles()) { return solver_->MakeLocalSearchPhase(nexts_, first_solution, parameters); } else { @@ -4157,57 +4496,62 @@ DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder() { } } -void RoutingModel::SetupDecisionBuilders() { - if (FLAGS_routing_dfs) { - solve_db_ = GetFirstSolutionDecisionBuilder(); +void RoutingModel::SetupDecisionBuilders( + const RoutingSearchParameters& search_parameters) { + if (search_parameters.use_depth_first_search()) { + solve_db_ = GetFirstSolutionDecisionBuilder(search_parameters); } else { - solve_db_ = CreateLocalSearchDecisionBuilder(); + solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters); } CHECK(preassignment_ != nullptr); DecisionBuilder* restore_preassignment = solver_->MakeRestoreAssignment(preassignment_); solve_db_ = solver_->Compose(restore_preassignment, solve_db_); - improve_db_ = solver_->Compose( - restore_preassignment, - solver_->MakeLocalSearchPhase(GetOrCreateAssignment(), - CreateLocalSearchParameters())); + improve_db_ = + solver_->Compose(restore_preassignment, + solver_->MakeLocalSearchPhase( + GetOrCreateAssignment(), + CreateLocalSearchParameters(search_parameters))); restore_assignment_ = solver_->Compose(solver_->MakeRestoreAssignment(GetOrCreateAssignment()), CreateSolutionFinalizer()); } -void RoutingModel::SetupMetaheuristics() { +void RoutingModel::SetupMetaheuristics( + const RoutingSearchParameters& search_parameters) { SearchMonitor* optimize; - const RoutingMetaheuristic metaheuristic = GetSelectedMetaheuristic(); - VLOG(1) << "Using metaheuristic: " << RoutingMetaheuristicName(metaheuristic); + const LocalSearchMetaheuristic::Value metaheuristic = + search_parameters.local_search_metaheuristic(); switch (metaheuristic) { - case ROUTING_GUIDED_LOCAL_SEARCH: + case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH: if (CostsAreHomogeneousAcrossVehicles()) { optimize = solver_->MakeGuidedLocalSearch( false, cost_, [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); }, - FLAGS_routing_optimization_step, nexts_, - FLAGS_routing_guided_local_search_lambda_coefficient); + search_parameters.optimization_step(), nexts_, + search_parameters.guided_local_search_lambda_coefficient()); } else { optimize = solver_->MakeGuidedLocalSearch( false, cost_, [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); + search_parameters.optimization_step(), nexts_, vehicle_vars_, + search_parameters.guided_local_search_lambda_coefficient()); } break; - case ROUTING_SIMULATED_ANNEALING: + case LocalSearchMetaheuristic::SIMULATED_ANNEALING: optimize = solver_->MakeSimulatedAnnealing( - false, cost_, FLAGS_routing_optimization_step, 100); + false, cost_, search_parameters.optimization_step(), 100); break; - case ROUTING_TABU_SEARCH: - optimize = solver_->MakeTabuSearch( - false, cost_, FLAGS_routing_optimization_step, nexts_, 10, 10, .8); + case LocalSearchMetaheuristic::TABU_SEARCH: + optimize = solver_->MakeTabuSearch(false, cost_, + search_parameters.optimization_step(), + nexts_, 10, 10, .8); break; default: - optimize = solver_->MakeMinimize(cost_, FLAGS_routing_optimization_step); + optimize = + solver_->MakeMinimize(cost_, search_parameters.optimization_step()); } monitors_.push_back(optimize); } @@ -4245,16 +4589,20 @@ void RoutingModel::SetupTrace() { } } -void RoutingModel::SetupSearchMonitors() { +void RoutingModel::SetupSearchMonitors( + const RoutingSearchParameters& search_parameters) { monitors_.push_back(GetOrCreateLimit()); - SetupMetaheuristics(); + SetupMetaheuristics(search_parameters); SetupAssignmentCollector(); SetupTrace(); } -bool RoutingModel::UsesLightPropagation() const { - return FLAGS_routing_use_light_propagation && !FLAGS_routing_dfs && - GetSelectedFirstSolutionStrategy() != ROUTING_DEFAULT_STRATEGY; +bool RoutingModel::UsesLightPropagation( + const RoutingSearchParameters& search_parameters) const { + return search_parameters.use_light_propagation() && + !search_parameters.use_depth_first_search() && + search_parameters.first_solution_strategy() != + FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE; } void RoutingModel::AddVariableMinimizedByFinalizer(IntVar* var) { @@ -4267,9 +4615,10 @@ void RoutingModel::AddVariableMaximizedByFinalizer(IntVar* var) { variables_maximized_by_finalizer_.push_back(var); } -void RoutingModel::SetupSearch() { - SetupDecisionBuilders(); - SetupSearchMonitors(); +void RoutingModel::SetupSearch( + const RoutingSearchParameters& search_parameters) { + SetupDecisionBuilders(search_parameters); + SetupSearchMonitors(search_parameters); } void RoutingModel::AddToAssignment(IntVar* const var) { @@ -4300,6 +4649,25 @@ RoutingModel::NodeEvaluator2* RoutingModel::NewCachedCallback( } } +// NewCachedStateDependentCallback returns a new evaluator creating at most one +// RangeIntToIntFunction per pair of nodes. The evaluator manages the cached +// functions, while the routing model takes owenership of both the old and the +// new callbacks. +RoutingModel::VariableNodeEvaluator2* +RoutingModel::NewCachedStateDependentCallback( + VariableNodeEvaluator2* callback) { + const int size = node_to_index_.size(); + VariableNodeEvaluator2* cached_evaluator = nullptr; + if (!FindCopy(cached_state_dependent_callbacks_, callback, + &cached_evaluator)) { + cached_evaluator = new StateDependentRoutingCache(callback, size); + cached_state_dependent_callbacks_[callback] = cached_evaluator; + owned_state_dependent_callbacks_.insert(callback); + owned_state_dependent_callbacks_.insert(cached_evaluator); + } + return cached_evaluator; +} + // BEGIN(DEPRECATED) // Deprecated RoutingModel methods. See the .h. // DON'T REMOVE RASHLY! These methods might still be used by old open-source @@ -4377,8 +4745,8 @@ int64 RoutingModel::GetCumulVarSoftUpperBound(NodeIndex node, int64 RoutingModel::GetCumulVarSoftUpperBoundCoefficient( NodeIndex node, const std::string& name) const { return HasDimension(name) - ? GetDimensionOrDie(name) - .GetCumulVarSoftUpperBoundCoefficient(node) + ? GetDimensionOrDie(name).GetCumulVarSoftUpperBoundCoefficient( + node) : 0; } void RoutingModel::SetStartCumulVarSoftUpperBound(int vehicle, @@ -4422,8 +4790,8 @@ int64 RoutingModel::GetEndCumulVarSoftUpperBound(int vehicle, int64 RoutingModel::GetEndCumulVarSoftUpperBoundCoefficient( int vehicle, const std::string& name) const { return HasDimension(name) - ? GetDimensionOrDie(name) - .GetEndCumulVarSoftUpperBoundCoefficient(vehicle) + ? GetDimensionOrDie(name).GetEndCumulVarSoftUpperBoundCoefficient( + vehicle) : 0; } IntVar* RoutingModel::CumulVar(int64 index, const std::string& name) const { @@ -4436,21 +4804,33 @@ IntVar* RoutingModel::TransitVar(int64 index, const std::string& name) const { IntVar* RoutingModel::SlackVar(int64 index, const std::string& name) const { return HasDimension(name) ? GetDimensionOrDie(name).SlackVar(index) : nullptr; } + // END(DEPRECATED) -RoutingDimension::RoutingDimension(RoutingModel* model, const std::string& name) - : global_span_cost_coefficient_(0), model_(model), name_(name) { +RoutingDimension::RoutingDimension(RoutingModel* model, const std::string& name, + const RoutingDimension* base_dimension) + : base_dimension_(base_dimension), + global_span_cost_coefficient_(0), + model_(model), + name_(name) { CHECK(model != nullptr); vehicle_span_upper_bounds_.assign(model->vehicles(), kint64max); vehicle_span_cost_coefficients_.assign(model->vehicles(), 0); } +RoutingDimension::RoutingDimension(RoutingModel* model, const std::string& name, + SelfBased) + : RoutingDimension(model, name, this) {} + void RoutingDimension::Initialize( RoutingModel::VehicleEvaluator* vehicle_capacity, int64 capacity, const std::vector& transit_evaluators, + const std::vector& + state_dependent_node_evaluators, int64 slack_max) { InitializeCumuls(vehicle_capacity, capacity); - InitializeTransits(transit_evaluators, slack_max); + InitializeTransits(transit_evaluators, state_dependent_node_evaluators, + slack_max); } namespace { @@ -4539,22 +4919,11 @@ void RoutingDimension::InitializeCumuls( Solver* const solver = model_->solver(); const int size = model_->Size() + model_->vehicles(); solver->MakeIntVarArray(size, 0LL, capacity, name_, &cumuls_); + capacity_vars_.clear(); if (vehicle_capacity != nullptr) { + solver->MakeIntVarArray(size, 0LL, kint64max, &capacity_vars_); for (int i = 0; i < size; ++i) { - IntVar* capacity_var = nullptr; - if (model_->UsesLightPropagation()) { - capacity_var = solver->MakeIntVar(0, kint64max); - solver->AddConstraint(MakeLightElement( - solver, capacity_var, model_->VehicleVar(i), - [vehicle_capacity](int64 vehicle) { - return vehicle >= 0 ? vehicle_capacity->Run(vehicle) : kint64max; - })); - } else { - capacity_var = - solver->MakeElement([vehicle_capacity](int64 index) { - return WrappedVehicleEvaluator(vehicle_capacity, index); - }, model_->VehicleVar(i))->Var(); - } + IntVar* const capacity_var = capacity_vars_[i]; if (i < model_->Size()) { IntVar* const capacity_active = solver->MakeBoolVar(); solver->AddConstraint( @@ -4572,10 +4941,12 @@ void RoutingDimension::InitializeCumuls( } namespace { -int64 WrappedEvaluator(RoutingModel* model, - RoutingModel::NodeEvaluator2* evaluator, int64 from, - int64 to) { - DCHECK(evaluator != nullptr); +template +auto WrappedEvaluator(RoutingModel* model, NodeEvaluator* evaluator, int64 from, + int64 to) + -> decltype(evaluator->Run(model->IndexToNode(to), + model->IndexToNode(to))) { + CHECK(evaluator != nullptr); return evaluator->Run(model->IndexToNode(from), model->IndexToNode(to)); } @@ -4583,92 +4954,210 @@ template int64 IthElementOrValue(const std::vector& v, int64 index) { return index >= 0 ? v[index] : value; } + +template +void ComputeTransitClasses( + const std::vector& node_evaluators, RoutingModel* const model, + std::vector* transit_evaluators, + std::vector>* class_evaluators, + std::vector* vehicle_to_class) { + CHECK(model != nullptr); + CHECK(transit_evaluators != nullptr); + CHECK(class_evaluators != nullptr); + CHECK(vehicle_to_class != nullptr); + transit_evaluators->clear(); + class_evaluators->clear(); + vehicle_to_class->resize(node_evaluators.size(), -1); + hash_map evaluator_to_class; + for (int i = 0; i < node_evaluators.size(); ++i) { + NodeEvaluator* const evaluator = node_evaluators[i]; + int evaluator_class = -1; + if (!FindCopy(evaluator_to_class, evaluator, &evaluator_class)) { + evaluator_class = class_evaluators->size(); + evaluator_to_class[evaluator] = evaluator_class; + class_evaluators->emplace_back(NewPermanentCallback( + &WrappedEvaluator, model, evaluator)); + } + (*vehicle_to_class)[i] = evaluator_class; + transit_evaluators->push_back((*class_evaluators)[evaluator_class].get()); + } +} } // namespace void RoutingDimension::InitializeTransits( - const std::vector& transit_evaluators, + const std::vector& node_evaluators, + const std::vector& + state_dependent_node_evaluators, int64 slack_max) { - CHECK_EQ(model_->vehicles(), transit_evaluators.size()); - for (const RoutingModel::NodeEvaluator2* const evaluator : - transit_evaluators) { - CHECK(evaluator != nullptr); - evaluator->CheckIsRepeatable(); + CHECK_EQ(model_->vehicles(), node_evaluators.size()); + CHECK(base_dimension_ == nullptr || + model_->vehicles() == state_dependent_node_evaluators.size()); + for (int64 index = 0; index < model_->vehicles(); ++index) { + CHECK(node_evaluators[index] != nullptr); + CHECK(node_evaluators[index]->IsRepeatable()); + CHECK(base_dimension_ == nullptr || + state_dependent_node_evaluators[index] != nullptr); + CHECK(base_dimension_ == nullptr || + state_dependent_node_evaluators[index]->IsRepeatable()); } Solver* const solver = model_->solver(); const int size = model_->Size(); - transits_.resize(size); - slacks_.resize(size); - // Compute transit classes - class_evaluators_.clear(); - transit_evaluators_.clear(); - hash_map evaluator_to_class; - 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; - if (!FindCopy(evaluator_to_class, evaluator, &evaluator_class)) { - evaluator_class = class_evaluators_.size(); - evaluator_to_class[evaluator] = evaluator_class; - class_evaluators_.emplace_back( - NewPermanentCallback(&WrappedEvaluator, model_, evaluator)); - } - vehicle_to_class_[i] = evaluator_class; - transit_evaluators_.push_back(class_evaluators_[evaluator_class].get()); + transits_.resize(size, nullptr); + fixed_transits_.resize(size, nullptr); + slacks_.resize(size, nullptr); + dependent_transits_.resize(size, nullptr); + ComputeTransitClasses(node_evaluators, model_, &transit_evaluators_, + &class_evaluators_, &vehicle_to_class_); + if (base_dimension_ != nullptr) { + ComputeTransitClasses(state_dependent_node_evaluators, model_, + &state_dependent_transit_evaluators_, + &state_dependent_class_evaluators_, + &state_dependent_vehicle_to_class_); } - Solver::IndexEvaluator1 vehicle_class_function = [this](int index) { - return IthElementOrValue<-1>(vehicle_to_class_, index); + + const Solver::IndexEvaluator1 dependent_vehicle_class_function = [this]( + int index) { + return (0 <= index && index < state_dependent_vehicle_to_class_.size()) + ? state_dependent_vehicle_to_class_[index] + : state_dependent_class_evaluators_.size(); }; CHECK(!class_evaluators_.empty()); - for (int i = 0; i < size; ++i) { - IntVar* fixed_transit = nullptr; - Solver::IndexEvaluator2 transit_vehicle_evaluator = [this, i]( + CHECK(base_dimension_ == nullptr || + !state_dependent_class_evaluators_.empty()); + const std::string slack_name = name_ + " slack"; + const std::string transit_name = name_ + " fixed transit"; + + for (int64 i = 0; i < size; ++i) { + fixed_transits_[i] = solver->MakeIntVar(kint64min, kint64max, transit_name); + // Setting dependent_transits_[i]. + if (base_dimension_ != nullptr) { + if (state_dependent_class_evaluators_.size() == 1) { + std::vector transition_variables(cumuls_.size(), nullptr); + for (int64 j = 0; j < cumuls_.size(); ++j) { + transition_variables[j] = + MakeRangeMakeElementExpr( + state_dependent_transit_evaluators_[0]->Run(i, j).transit, + base_dimension_->CumulVar(i), solver) + ->Var(); + } + dependent_transits_[i] = + solver->MakeElement(transition_variables, model_->NextVar(i)) + ->Var(); + } else { + IntVar* const vehicle_class_var = + solver->MakeElement(dependent_vehicle_class_function, + model_->VehicleVar(i)) + ->Var(); + std::vector transit_for_vehicle; + transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() + + 1); + for (const auto& evaluator : state_dependent_class_evaluators_) { + std::vector transition_variables(cumuls_.size(), nullptr); + for (int64 j = 0; j < cumuls_.size(); ++j) { + transition_variables[j] = + MakeRangeMakeElementExpr(evaluator->Run(i, j).transit, + base_dimension_->CumulVar(i), solver) + ->Var(); + } + transit_for_vehicle.push_back( + solver->MakeElement(transition_variables, model_->NextVar(i)) + ->Var()); + } + transit_for_vehicle.push_back(solver->MakeIntConst(Zero())); + dependent_transits_[i] = + solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var(); + } + } else { + dependent_transits_[i] = solver->MakeIntConst(Zero()); + } + + // Summing fixed transits, dependent transits and the slack. + IntExpr* transit_expr = fixed_transits_[i]; + if (dependent_transits_[i]->Min() != 0 || + dependent_transits_[i]->Max() != 0) { + transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]); + } + + if (slack_max == 0) { + slacks_[i] = solver->MakeIntConst(Zero()); + } else { + slacks_[i] = solver->MakeIntVar(0, slack_max, slack_name); + transit_expr = solver->MakeSum(slacks_[i], transit_expr); + } + transits_[i] = transit_expr->Var(); + } +} + +void RoutingDimension::CloseModel(bool use_light_propagation) { + Solver* const solver = model_->solver(); + RoutingModel::VehicleEvaluator* const vehicle_capacity = + capacity_evaluator_.get(); + for (int i = 0; i < capacity_vars_.size(); ++i) { + IntVar* const vehicle_var = model_->VehicleVar(i); + IntVar* const capacity_var = capacity_vars_[i]; + if (use_light_propagation) { + solver->AddConstraint(MakeLightElement( + solver, capacity_var, vehicle_var, [vehicle_capacity](int64 vehicle) { + return vehicle >= 0 ? vehicle_capacity->Run(vehicle) : kint64max; + })); + } else { + solver->AddConstraint(solver->MakeEquality( + capacity_var, solver->MakeElement( + [vehicle_capacity](int64 vehicle) { + return WrappedVehicleEvaluator( + vehicle_capacity, vehicle); + }, + vehicle_var) + ->Var())); + } + } + const Solver::IndexEvaluator1 vehicle_class_function = [this](int index) { + return IthElementOrValue<-1>(vehicle_to_class_, index); + }; + for (int i = 0; i < fixed_transits_.size(); ++i) { + IntVar* const next_var = model_->NextVar(i); + IntVar* const fixed_transit = fixed_transits_[i]; + const 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 (use_light_propagation) { if (class_evaluators_.size() == 1) { - fixed_transit = solver->MakeIntVar(kint64min, kint64max); - RoutingModel::NodeEvaluator2* const transit_evaluator = - transit_evaluators[0]; - solver->AddConstraint(MakeLightElement( - solver, fixed_transit, model_->NextVar(i), - [this, transit_evaluator, i](int64 to) { - return transit_evaluator->Run(model_->IndexToNode(i), - model_->IndexToNode(to)); - })); + RoutingModel::TransitEvaluator2* const transit_evaluator = + class_evaluators_[0].get(); + solver->AddConstraint( + MakeLightElement(solver, fixed_transit, next_var, + [this, transit_evaluator, i](int64 to) { + return transit_evaluator->Run(i, to); + })); } else { - fixed_transit = solver->MakeIntVar(kint64min, kint64max); - solver->AddConstraint(MakeLightElement2( - solver, fixed_transit, model_->NextVar(i), model_->VehicleVar(i), - transit_vehicle_evaluator)); + solver->AddConstraint(MakeLightElement2(solver, fixed_transit, next_var, + model_->VehicleVar(i), + transit_vehicle_evaluator)); } } else { if (class_evaluators_.size() == 1) { - fixed_transit = - solver->MakeElement([this, transit_evaluators, i](int64 index) { - return WrappedEvaluator(model_, transit_evaluators[0], - static_cast(i), index); - }, model_->NextVar(i))->Var(); + RoutingModel::TransitEvaluator2* const transit_evaluator = + class_evaluators_[0].get(); + solver->AddConstraint(solver->MakeEquality( + fixed_transit, solver->MakeElement( + [this, transit_evaluator, i](int64 to) { + return transit_evaluator->Run(i, to); + }, + model_->NextVar(i)) + ->Var())); } else { IntVar* const vehicle_class_var = solver->MakeElement(vehicle_class_function, model_->VehicleVar(i)) ->Var(); - fixed_transit = - solver->MakeElement(transit_vehicle_evaluator, model_->NextVar(i), - vehicle_class_var) - ->Var(); + solver->AddConstraint(solver->MakeEquality( + fixed_transit, solver->MakeElement(transit_vehicle_evaluator, + next_var, vehicle_class_var) + ->Var())); } } - if (slack_max == 0) { - transits_[i] = fixed_transit; - slacks_[i] = solver->MakeIntConst(Zero()); - } else { - IntVar* const slack_var = solver->MakeIntVar(0, slack_max, "slack"); - transits_[i] = solver->MakeSum(slack_var, fixed_transit)->Var(); - slacks_[i] = slack_var; - } } } @@ -5014,7 +5503,8 @@ void RoutingDimension::SetupGlobalSpanCost( } } -void RoutingDimension::SetupSlackCosts(std::vector* cost_elements) const { +void RoutingDimension::SetupSlackAndDependentTransitCosts( + std::vector* cost_elements) const { if (model_->vehicles() == 0) return; // Figure out whether all vehicles have the same span cost coefficient or not. bool all_vehicle_span_costs_are_equal = true; @@ -5029,16 +5519,32 @@ void RoutingDimension::SetupSlackCosts(std::vector* cost_elements) cons } // Make sure that the vehicle's start cumul will be maximized in the end; - // and that the vehicle's end cumul and the node's slacks wil be minimized. + // and that the vehicle's end cumul and the node's slacks will be minimized. // Note that we don't do that if there was no span cost (see the return // clause above), because in that case we want the dimension cumul to - // remain unconstrained. - for (int i = 0; i < model_->vehicles(); ++i) { - model_->AddVariableMaximizedByFinalizer(cumuls_[model_->Start(i)]); - model_->AddVariableMinimizedByFinalizer(cumuls_[model_->End(i)]); + // remain unconstrained. Since transitions depend on base dimensions, we + // have to make sure the slacks of base dimensions are taken care of. + // Also, it makes more sense to make decisions from the root of the tree + // towards to leaves, and hence the slacks are pushed in reverse order. + std::vector dimensions_with_relevant_slacks = {this}; + while (true) { + const RoutingDimension* next = + dimensions_with_relevant_slacks.back()->base_dimension_; + if (next == nullptr || next == dimensions_with_relevant_slacks.back()) { + break; + } + dimensions_with_relevant_slacks.push_back(next); } - for (IntVar* const slack : slacks_) { - model_->AddVariableMinimizedByFinalizer(slack); + + for (auto it = dimensions_with_relevant_slacks.rbegin(); + it != dimensions_with_relevant_slacks.rend(); ++it) { + for (int i = 0; i < model_->vehicles(); ++i) { + model_->AddVariableMaximizedByFinalizer((*it)->cumuls_[model_->Start(i)]); + model_->AddVariableMinimizedByFinalizer((*it)->cumuls_[model_->End(i)]); + } + for (IntVar* const slack : (*it)->slacks_) { + model_->AddVariableMinimizedByFinalizer(slack); + } } // Add the span cost element for the slacks (the transit component is already @@ -5049,18 +5555,26 @@ void RoutingDimension::SetupSlackCosts(std::vector* cost_elements) cons for (int var_index = 0; var_index < model_->Size(); ++var_index) { if (all_vehicle_span_costs_are_equal) { cost_elements->push_back( - solver->MakeProd(solver->MakeProd(slacks_[var_index], - vehicle_span_cost_coefficients_[0]), + solver->MakeProd(solver->MakeProd( + solver->MakeSum(slacks_[var_index], + dependent_transits_[var_index]), + vehicle_span_cost_coefficients_[0]), model_->ActiveVar(var_index)) ->Var()); } else { IntVar* cost_coefficient_var = - solver->MakeElement([this](int index) { - return IthElementOrValue<0LL>(vehicle_span_cost_coefficients_, - index); - }, 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()); + solver->MakeProd(solver->MakeSum(slacks_[var_index], + dependent_transits_[var_index]), + cost_coefficient_var) + ->Var()); // TODO(user): verify that there isn't any benefit in explicitly making // a product with ActiveVar(). There shouldn't be, since ActiveVar() == 0 // should be equivalent to VehicleVar() == -1. diff --git a/src/constraint_solver/routing.h b/src/constraint_solver/routing.h index e571701b45..bbb8acba2f 100644 --- a/src/constraint_solver/routing.h +++ b/src/constraint_solver/routing.h @@ -166,11 +166,13 @@ #include "base/integral_types.h" #include "base/logging.h" #include "base/macros.h" -#include "base/int_type_indexed_vector.h" #include "base/int_type.h" +#include "base/int_type_indexed_vector.h" #include "base/hash.h" #include "constraint_solver/constraint_solver.h" #include "constraint_solver/constraint_solveri.h" +#include "constraint_solver/routing_parameters.pb.h" +#include "util/range_query_function.h" #include "base/adjustable_priority_queue-inl.h" #include "base/adjustable_priority_queue.h" @@ -193,196 +195,8 @@ DEFINE_INT_TYPE(_RoutingModel_DimensionIndex, int); DEFINE_INT_TYPE(_RoutingModel_DisjunctionIndex, int); DEFINE_INT_TYPE(_RoutingModel_VehicleClassIndex, int); -// This class stores solver parameters. -struct RoutingParameters { - RoutingParameters() { - use_light_propagation = false; - cache_callbacks = false; - max_cache_size = 1000; - } - - // Use constraints with light propagation in routing model. - bool use_light_propagation; - // Cache callback calls. - bool cache_callbacks; - // Maximum cache size when callback caching is on. - int64 max_cache_size; -}; - -// This class stores search parameters. -struct RoutingSearchParameters { - RoutingSearchParameters() { - no_lns = false; - no_fullpathlns = true; - no_relocate = false; - no_relocate_neighbors = true; - no_exchange = false; - no_cross = false; - no_2opt = false; - no_oropt = false; - no_make_active = false; - no_lkh = false; - no_tsp = true; - no_tsplns = true; - use_chain_make_inactive = false; - use_extended_swap_active = false; - solution_limit = kint64max; - time_limit = kint64max; - lns_time_limit = 100; - guided_local_search = false; - guided_local_search_lambda_coefficient = 0.1; - simulated_annealing = false; - tabu_search = false; - dfs = false; - first_solution = ""; - use_first_solution_dive = false; - optimization_step = 1; - trace = false; - } - - // ----- Neighborhood deactivation ----- - - // Routing: forbids use of Large Neighborhood Search. - bool no_lns; - // Routing: forbids use of Full-path Large Neighborhood Search. - bool no_fullpathlns; - // Routing: forbids use of Relocate neighborhood. - bool no_relocate; - // Routing: forbids use of RelocateNeighbors neighborhood. - bool no_relocate_neighbors; - // Routing: forbids use of Exchange neighborhood. - bool no_exchange; - // Routing: forbids use of Cross neighborhood. - bool no_cross; - // Routing: forbids use of 2Opt neighborhood. - bool no_2opt; - // Routing: forbids use of OrOpt neighborhood. - bool no_oropt; - // Routing: forbids use of MakeActive/SwapActive/MakeInactive neighborhoods. - bool no_make_active; - // Routing: forbids use of LKH neighborhood. - bool no_lkh; - // Routing: forbids use of TSPOpt neighborhood. - bool no_tsp; - // Routing: forbids use of TSPLNS neighborhood. - bool no_tsplns; - // Routing: use chain version of MakeInactive neighborhood. - bool use_chain_make_inactive; - // Routing: use extended version of SwapActive neighborhood. - bool use_extended_swap_active; - - // ----- Search limits ----- - - // Routing: number of solutions limit. - int64 solution_limit; - // Routing: time limit in ms. - int64 time_limit; - // Routing: time limit in ms for LNS sub-decision builder. - int64 lns_time_limit; - - // Meta-heuristics - - // Routing: use GLS. - bool guided_local_search; - // Lambda coefficient in GLS. - double guided_local_search_lambda_coefficient; - // Routing: use simulated annealing. - bool simulated_annealing; - // Routing: use tabu search. - bool tabu_search; - - // ----- Search control ------ - - // Routing: use a complete depth-first search. - bool dfs; - // Routing: first solution heuristic. See RoutingStrategyName() in - // the code to get a full list. - std::string first_solution; - // Dive (left-branch) for first solution. - bool use_first_solution_dive; - // Optimization step. - int64 optimization_step; - // Trace search. - bool trace; -}; - class RoutingModel { public: - // First solution strategies, used as starting point of local search. - // TODO(user): Remove this when the corresponding enum is available in - // the routing search parameters protobuf. - enum RoutingStrategy { - // Select the first node with an unbound successor and connect it to the - // first available node. - // This is equivalent to the CHOOSE_FIRST_UNBOUND strategy combined with - // ASSIGN_MIN_VALUE (cf. constraint_solver.h). - ROUTING_DEFAULT_STRATEGY, - // Iteratively connect two nodes which produce the cheapest route segment. - ROUTING_GLOBAL_CHEAPEST_ARC, - // Select the first node with an unbound successor and connect it to the - // node which produces the cheapest route segment. - ROUTING_LOCAL_CHEAPEST_ARC, - // Starting from a route "start" node, connect it to the node which produces - // the cheapest route segment, then extend the route by iterating on the - // last node added to the route. - ROUTING_PATH_CHEAPEST_ARC, - // Same as ROUTING_PATH_CHEAPEST_ARC, but arcs are evaluated with a - // comparison-based selector which will favor the most constrained arc - // first. See ArcIsMoreConstrainedThanArc() for details. - ROUTING_PATH_MOST_CONSTRAINED_ARC, - // Same as ROUTING_PATH_CHEAPEST_ARC, except that arc costs are evaluated - // using the function passed to RoutingModel::SetFirstSolutionEvaluator(). - ROUTING_EVALUATOR_STRATEGY, - // Make all node inactive. Only finds a solution if nodes are optional (are - // element of a disjunction constraint with a finite penalty cost). - ROUTING_ALL_UNPERFORMED, - // Iteratively build a solution by inserting the cheapest node at its - // cheapest position; the cost of insertion is based on the global cost - // function of the routing model. As of 2/2012, only works on models with - // optional nodes (with finite penalty costs). - ROUTING_BEST_INSERTION, - // Iteratively build a solution by inserting the cheapest node at its - // cheapest position; the cost of insertion is based on the the arc cost - // function. Is faster than BEST_INSERTION. - ROUTING_GLOBAL_CHEAPEST_INSERTION, - // Iteratively build a solution by inserting each node at its cheapest - // position; the cost of insertion is based on the the arc cost function. - // Differs from ROUTING_GLOBAL_CHEAPEST_INSERTION by the node selected for - // insertion; here nodes are considered in their order of creation. Is - // faster than ROUTING_GLOBAL_CHEAPEST_INSERTION. - ROUTING_LOCAL_CHEAPEST_INSERTION, - // Savings algorithm (Clarke & Wright). - // Reference: Clarke, G. & Wright, J.W.: - // "Scheduling of Vehicles from a Central Depot to a Number of - // Delivery Points", Operations Research, Vol. 12, 1964, pp. 568-581 - ROUTING_SAVINGS, - // Sweep algorithm (Wren & Holliday). - // Reference: Anthony Wren & Alan Holliday: Computer Scheduling of Vehicles - // from One or More Depots to a Number of Delivery Points - // Operational Research Quarterly (1970-1977), - // Vol. 23, No. 3 (Sep., 1972), pp. 333-344 - ROUTING_SWEEP, - ROUTING_FIRST_SOLUTION_STRATEGY_COUNTER - }; - - // Metaheuristics used to guide the search. Apart greedy descent, they will - // try to escape local minima. - enum RoutingMetaheuristic { - // Accepts improving (cost-reducing) local search neighbors until a local - // minimum is reached. This is the default heuristic. - ROUTING_GREEDY_DESCENT, - // Uses guided local search to escape local minima - // (cf. http://en.wikipedia.org/wiki/Guided_Local_Search); this is - // generally the most efficient metaheuristic for vehicle routing. - ROUTING_GUIDED_LOCAL_SEARCH, - // Uses simulated annealing to escape local minima - // (cf. http://en.wikipedia.org/wiki/Simulated_annealing). - ROUTING_SIMULATED_ANNEALING, - // Uses tabu search to escape local minima - // (cf. http://en.wikipedia.org/wiki/Tabu_search). - ROUTING_TABU_SEARCH, - }; - // Status of the search. enum Status { // Problem not solved yet (before calling RoutingModel::Solve()). @@ -407,6 +221,28 @@ class RoutingModel { typedef ResultCallback2 TransitEvaluator2; typedef std::pair NodePair; typedef std::vector NodePairs; +#if !defined(SWIG) + // What follows is relevant for models with time/state dependent transits. + // Such transits, say from node A to node B, are functions f: int64->int64 + // of the cumuls of a dimension. The user is free to implement the abstract + // RangeIntToIntFunction interface, but it is expected that the implementation + // of each method is quite fast. For performance-related reasons, + // StateDependentTransit keeps an additional pointer to a + // RangeMinMaxIndexFunction, with similar functionality to + // RangeIntToIntFunction, for g(x) = f(x)+x, where f is the transit from A to + // B. In most situations the best solutions are problem-specific, but in case + // of doubt the user may use the MakeStateDependentTransit function from the + // routing library, which works out-of-the-box, with very good running time, + // but memory inefficient in some situations. + struct StateDependentTransit { + RangeIntToIntFunction* transit; // f(x) + RangeMinMaxIndexFunction* transit_plus_identity; // g(x) = f(x) + x + }; + typedef ResultCallback2 + VariableNodeEvaluator2; + typedef ResultCallback2 + VariableIndexEvaluator2; +#endif // SWIG #if !defined(SWIG) struct CostClass { @@ -495,23 +331,33 @@ class RoutingModel { // dimension name does not correspond to an actual dimension. static const DimensionIndex kNoDimension; + // In the following constructors, the versions which do not take + // RoutingModelParamters are equivalent to passing DefaultModelParameters. // Supposes a single depot. A depot is the start and end node of the route of // a vehicle. RoutingModel(int nodes, int vehicles); + RoutingModel(int nodes, int vehicles, + const RoutingModelParameters& parameters); // Constructor taking a vector of (start node, end node) pairs for each // vehicle route. Used to model multiple depots. RoutingModel(int nodes, int vehicles, const std::vector >& start_end); + RoutingModel(int nodes, int vehicles, + const std::vector >& start_end, + const RoutingModelParameters& parameters); // Constructor taking vectors of start nodes and end nodes for each // vehicle route. Used to model multiple depots. // TODO(user): added to simplify SWIG wrapping. Remove when swigging // std::vector > is ok. RoutingModel(int nodes, int vehicles, const std::vector& starts, const std::vector& ends); + RoutingModel(int nodes, int vehicles, const std::vector& starts, + const std::vector& ends, + const RoutingModelParameters& parameters); ~RoutingModel(); - // Global parameters. - static void SetGlobalParameters(const RoutingParameters& parameters); + static RoutingModelParameters DefaultModelParameters(); + static RoutingSearchParameters DefaultSearchParameters(); // Model creation @@ -583,9 +429,47 @@ class RoutingModel { // (and doesn't create the new dimension). bool AddMatrixDimension(const int64* const* values, int64 capacity, bool fix_start_cumul_to_zero, const std::string& name); +#if !defined(SWIG) + // Creates a dimension with transits depending on the cumuls of another + // dimension. 'pure_transits' are the per-vehicle fixed transits as above. + // 'dependent_transits' is a vector containing for each vehicle a function + // taking two nodes and returning a function taking a dimension cumul value + // and returning a transit value. 'base_dimension' indicates the dimension + // from which the cumul variable is taken. If 'base_dimension' is nullptr, + // then the newly created dimension is self-based. + bool AddDimensionDependentDimensionWithVehicleCapacity( + const std::vector& pure_transits, + const std::vector& dependent_transits, + const RoutingDimension* base_dimension, int64 slack_max, + VehicleEvaluator* vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name) { + return AddDimensionDependentDimensionWithVehicleCapacityInternal( + pure_transits, dependent_transits, base_dimension, slack_max, kint64max, + vehicle_capacity, fix_start_cumul_to_zero, name); + } + // As above, but pure_transits are taken to be zero evaluators. + bool AddDimensionDependentDimensionWithVehicleCapacity( + const std::vector& transits, + const RoutingDimension* base_dimension, int64 slack_max, + VehicleEvaluator* vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name); + // Homogeneous versions of the functions above. + bool AddDimensionDependentDimensionWithVehicleCapacity( + VariableNodeEvaluator2* transits, const RoutingDimension* base_dimension, + int64 slack_max, int64 vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name); + bool AddDimensionDependentDimensionWithVehicleCapacity( + NodeEvaluator2* pure_transits, VariableNodeEvaluator2* dependent_transits, + const RoutingDimension* base_dimension, int64 slack_max, + int64 vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& name); + // Creates a cached StateDependentTransit from an std::function. + static RoutingModel::StateDependentTransit MakeStateDependentTransit( + const std::function& f, int64 domain_start, + int64 domain_end); +#endif // SWIG // Outputs the names of all dimensions added to the routing engine. // TODO(user): rename. - void GetAllDimensions(std::vector* dimension_names) const; + std::vector GetAllDimensionNames() const; // Returns true if a dimension exists for a given dimension name. bool HasDimension(const std::string& dimension_name) const; // Returns a dimension from its name. Dies if the dimension does not exist. @@ -641,13 +525,13 @@ class RoutingModel { } // Returns the variable indices of the nodes in the same disjunction as the // node corresponding to the variable of index 'index'. - void GetDisjunctionIndicesFromIndex(int64 index, std::vector* indices) const { + std::vector GetDisjunctionIndicesFromIndex(int64 index) const { + std::vector disjunction_indices; DisjunctionIndex disjunction = kNoDisjunction; if (GetDisjunctionIndexFromVariableIndex(index, &disjunction)) { - *indices = disjunctions_[disjunction].nodes; - } else { - indices->clear(); + disjunction_indices = disjunctions_[disjunction].nodes; } + return disjunction_indices; } #if !defined(SWIGPYTHON) && !defined(SWIGJAVA) // Returns the variable indices of the nodes in the disjunction of index @@ -723,15 +607,7 @@ class RoutingModel { // the first and last nodes. int64 GetFixedCostOfVehicle(int vehicle) const; - // Search - // Returns the strategy used to build a first solution. - RoutingStrategy first_solution_strategy() const { - return first_solution_strategy_; - } - // Sets the strategy used to build a first solution. - void set_first_solution_strategy(RoutingStrategy strategy) { - first_solution_strategy_ = strategy; - } +// Search // Gets/sets the evaluator used when the first solution heuristic is set to // ROUTING_EVALUATOR_STRATEGY (variant of ROUTING_PATH_CHEAPEST_ARC using // 'evaluator' to sort node segments). @@ -744,22 +620,9 @@ class RoutingModel { 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 - // was set. - RoutingStrategy GetSelectedFirstSolutionStrategy() const; // Adds a local search operator to the set of operators used to solve the // vehicle routing problem. void AddLocalSearchOperator(LocalSearchOperator* ls_operator); - // Returns the metaheuristic used. - RoutingMetaheuristic metaheuristic() const { return metaheuristic_; } - // Sets the metaheuristic to be used. - void set_metaheuristic(RoutingMetaheuristic metaheuristic) { - metaheuristic_ = metaheuristic; - } - // If a metaheuristic flag has been set, returns the corresponding - // metaheuristic, otherwise returns the metaheuristic which was set. - RoutingMetaheuristic GetSelectedMetaheuristic() const; // Adds a search monitor to the search used to solve the routing model. void AddSearchMonitor(SearchMonitor* const monitor); // Adds a variable to minimize in the solution finalizer. The solution @@ -774,14 +637,25 @@ class RoutingModel { // modification to the model can be done, but RoutesToAssignment becomes // available. Note that CloseModel() is automatically called by Solve() and // other methods that produce solution. + // This is equivalent to calling + // CloseModelWithParameters(DefaultSearchParameters()). void CloseModel(); + // Same as above taking search parameters (as of 10/2015 some the parameters + // have to be set when closing the model). + void CloseModelWithParameters( + const RoutingSearchParameters& search_parameters); // Solves the current routing model; closes the current model. + // This is equivalent to calling + // SolveWithParameters(DefaultSearchParameters()) + // or + // SolveFromAssignmentWithParameters(assignment, DefaultSearchParameters()). const Assignment* Solve(const Assignment* assignment = nullptr); // Solves the current routing model with the given parameters. - // Closes the current model. const Assignment* SolveWithParameters( - const RoutingSearchParameters& parameters, - const Assignment* assignment); + const RoutingSearchParameters& search_parameters); + const Assignment* SolveFromAssignmentWithParameters( + const Assignment* assignment, + const RoutingSearchParameters& search_parameters); // Computes a lower bound to the routing problem solving a linear assignment // problem. The routing model must be closed before calling this method. // Note that problems with node disjunction constraints (including optional @@ -873,10 +747,13 @@ class RoutingModel { // solution exists. // This method changes the vehicle and dimension variables as necessary. // While compacting the solution, only basic checks on vehicle variables are - // performed; the complete solution is checked at the end and if it is not - // valid, no attempts to repair it are made (instead, the method returns - // nullptr). + // performed; if one of these checks fails no attempts to repair it are made + // (instead, the method returns nullptr). Assignment* CompactAssignment(const Assignment& assignment) const; + // Same as CompactAssignment() but also checks the validity of the final + // compact solution; if it is not valid, no attempts to repair it are made + // (instead, the method returns nullptr). + Assignment* CompactAndCheckAssignment(const Assignment& assignment) const; // Adds an extra variable to the vehicle routing assignment. void AddToAssignment(IntVar* const var); void AddIntervalToAssignment(IntervalVar* const interval); @@ -1022,30 +899,12 @@ class RoutingModel { // nodes that are not end of a route are safe. bool HasIndex(NodeIndex node) const; - // Time limits - // Returns the current time limit used in the search. - int64 TimeLimit() const { return time_limit_ms_; } - // Updates the time limit used in the search. - void UpdateTimeLimit(int64 limit_ms); - // Updates the time limit used in the Large Neighborhood search tree. - void UpdateLNSTimeLimit(int64 limit_ms); - // Returns statistics on first solution search, number of decisions sent to // filters, number of decisions rejected by filters. - int64 GetNumberOfDecisionsInFirstSolution() const; - int64 GetNumberofRejectsInFirstSolution() const; - - // Conversion between enums and strings; the Parse*() conversions return true - // on success and the *Name() conversions return nullptr when given unknown - // values. See the .cc for the name conversions. The rule of thumb is: - // RoutingModel::ROUTING_PATH_CHEAPEST_ARC <-> "PathCheapestArc". - static const char* RoutingStrategyName(RoutingStrategy strategy); - static bool ParseRoutingStrategy(const std::string& strategy_str, - RoutingStrategy* strategy); - static const char* RoutingMetaheuristicName( - RoutingMetaheuristic metaheuristic); - static bool ParseRoutingMetaheuristic(const std::string& metaheuristic_str, - RoutingMetaheuristic* metaheuristic); + int64 GetNumberOfDecisionsInFirstSolution( + const RoutingSearchParameters& search_parameters) const; + int64 GetNumberOfRejectsInFirstSolution( + const RoutingSearchParameters& search_parameters) const; // DEPRECATED METHODS. // Don't use these methods; instead use their proposed replacement (in @@ -1062,6 +921,7 @@ class RoutingModel { int GetVehicleCostCount() const; // GetNonZeroCostClassesCount() int64 GetCost(int64 i, int64 j, int64 v); // GetArcCostForVehicle() int64 GetVehicleClassCost(int64 i, int64 j, int64 c); // GetArcCostForClass() + // All the methods below are replaced by public methods of the // RoutingDimension class. See that class. void SetDimensionTransitCost(const std::string& d, int64 c); @@ -1091,30 +951,70 @@ class RoutingModel { int64 GetEndCumulVarSoftUpperBound(int, const std::string&) const; int64 GetEndCumulVarSoftUpperBoundCoefficient(int, const std::string&) const; + // The next few members are in the public section only for testing purposes. + // TODO(user): Find a way to test and restrict the access at the same time. + // + // MakeGuidedSlackFinalizer creates a DecisionBuilder for the slacks of a + // dimension using a callback to choose which values to start with. + // The finalizer works only when all next variables in the model have + // been fixed. It has the following two characteristics: + // 1. It follows the routes defined by the nexts variables when choosing a + // variable to make a decision on. + // 2. When it comes to choose a value for the slack of node i, the decision + // builder first calls the callback with argument i, and supposingly the + // returned value is x it creates decisions slack[i] = x, slack[i] = x + 1, + // slack[i] = x - 1, slack[i] = x + 2, etc. + DecisionBuilder* MakeGuidedSlackFinalizer( + const RoutingDimension* dimension, + std::function initializer); +#ifndef SWIG + // TODO(user): MakeGreedyDescentLSOperator is too general for routing.h. + // Perhaps move it to constraint_solver.h. + // MakeGreedyDescentLSOperator creates a local search operator that tries to + // improve the initial assignment by moving a logarithmically decreasing step + // away in each possible dimension. + static std::unique_ptr MakeGreedyDescentLSOperator( + std::vector variables); +#endif // __SWIG__ + // MakeSelfDependentDimensionFinalizer is a finalizer for the slacks of a + // self-dependent dimension. It makes an extensive use of the caches of the + // state dependent transits. + // In detail, MakeSelfDependentDimensionFinalizer returns a composition of a + // local search decision builder with a greedy descent operator for the cumul + // of the start of each route and a guided slack finalizer. Provided there are + // no time windows and the maximum slacks are large enough, once the cumul of + // the start of route is fixed, the guided finalizer can find optimal values + // of the slacks for the rest of the route in time proportional to the length + // of the route. Therefore the composed finalizer generally works in time + // O(log(t)*n*m), where t is the latest possible departute time, n is the + // number of nodes in the network and m is the number of vehicles. + DecisionBuilder* MakeSelfDependentDimensionFinalizer( + const RoutingDimension* dimension); + private: // Local search move operator usable in routing. - // TODO(user): Remove this when the corresponding enum is available in - // the routing search parameters protobuf. enum RoutingLocalSearchOperator { - ROUTING_RELOCATE = 0, - ROUTING_PAIR_RELOCATE, - ROUTING_RELOCATE_NEIGHBORS, - ROUTING_EXCHANGE, - ROUTING_CROSS, - ROUTING_TWO_OPT, - ROUTING_OR_OPT, - ROUTING_LKH, - ROUTING_TSP_OPT, - ROUTING_TSP_LNS, - ROUTING_PATH_LNS, - ROUTING_FULL_PATH_LNS, - ROUTING_INACTIVE_LNS, - ROUTING_MAKE_ACTIVE, - ROUTING_MAKE_INACTIVE, - ROUTING_MAKE_CHAIN_INACTIVE, - ROUTING_SWAP_ACTIVE, - ROUTING_EXTENDED_SWAP_ACTIVE, - ROUTING_LOCAL_SEARCH_OPERATOR_COUNTER + RELOCATE = 0, + RELOCATE_PAIR, + RELOCATE_NEIGHBORS, + EXCHANGE, + CROSS, + CROSS_EXCHANGE, + TWO_OPT, + OR_OPT, + LIN_KERNIGHAN, + TSP_OPT, + MAKE_ACTIVE, + MAKE_INACTIVE, + MAKE_CHAIN_INACTIVE, + SWAP_ACTIVE, + EXTENDED_SWAP_ACTIVE, + NODE_PAIR_SWAP, + PATH_LNS, + FULL_PATH_LNS, + TSP_LNS, + INACTIVE_LNS, + LOCAL_SEARCH_OPERATOR_COUNTER }; // Structure storing a value for a set of nodes. Is used to store for node @@ -1145,15 +1045,29 @@ class RoutingModel { const std::vector& evaluators, int64 slack_max, int64 capacity, VehicleEvaluator* vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& dimension_name); + bool AddDimensionDependentDimensionWithVehicleCapacityInternal( + const std::vector& pure_transits, + const std::vector& dependent_transits, + const RoutingDimension* base_dimension, int64 slack_max, int64 capacity, + VehicleEvaluator* vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name); + bool InitializeDimensionInternal( + const std::vector& evaluators, + const std::vector& state_dependent_evaluators, + int64 slack_max, int64 capacity, VehicleEvaluator* vehicle_capacity, + bool fix_start_cumul_to_zero, RoutingDimension* dimension); DimensionIndex GetDimensionIndex(const std::string& dimension_name) const; - uint64 GetFingerprintOfEvaluator(NodeEvaluator2* evaluator) const; - void ComputeCostClasses(); + uint64 GetFingerprintOfEvaluator(NodeEvaluator2* evaluator, + bool fingerprint_arc_cost_evaluators) const; + void ComputeCostClasses(const RoutingSearchParameters& parameters); void ComputeVehicleClasses(); int64 GetArcCostForClassInternal(int64 from_index, int64 to_index, CostClassIndex cost_class_index); - void AppendHomogeneousArcCosts(int node_index, + void AppendHomogeneousArcCosts(const RoutingSearchParameters& parameters, + int node_index, std::vector* cost_elements); - void AppendArcCosts(int node_index, std::vector* cost_elements); + void AppendArcCosts(const RoutingSearchParameters& parameters, int node_index, + std::vector* cost_elements); Assignment* DoRestoreAssignment(); static const CostClassIndex kCostClassIndexOfZeroCost; int64 SafeGetCostClassInt64OfVehicle(int64 vehicle) const { @@ -1187,17 +1101,26 @@ class RoutingModel { Assignment* compact_assignment) const; NodeEvaluator2* NewCachedCallback(NodeEvaluator2* callback); + VariableNodeEvaluator2* NewCachedStateDependentCallback( + VariableNodeEvaluator2* callback); void CheckDepot(); - void QuietCloseModel() { + void QuietCloseModel(); + void QuietCloseModelWithParameters( + const RoutingSearchParameters& parameters) { if (!closed_) { - CloseModel(); + CloseModelWithParameters(parameters); } } + // See CompactAssignment. Checks the final solution if + // check_compact_assignement is true. + Assignment* CompactAssignmentInternal(const Assignment& assignment, + bool check_compact_assignment) const; // Check that current search parameters are valid. If not, the status of the // solver is set to ROUTING_INVALID. - bool ValidateSearchParameters(); + bool ValidateSearchParameters( + const RoutingSearchParameters& search_parameters); // Sets up search objects, such as decision builders and monitors. - void SetupSearch(); + void SetupSearch(const RoutingSearchParameters& search_parameters); // Set of auxiliary methods used to setup the search. // TODO(user): Document each auxiliary method. Assignment* GetOrCreateAssignment(); @@ -1206,22 +1129,28 @@ class RoutingModel { SearchLimit* GetOrCreateLargeNeighborhoodSearchLimit(); LocalSearchOperator* CreateInsertionOperator(); void CreateNeighborhoodOperators(); - LocalSearchOperator* GetNeighborhoodOperators() const; + LocalSearchOperator* GetNeighborhoodOperators( + const RoutingSearchParameters& search_parameters) const; const std::vector& GetOrCreateLocalSearchFilters(); const std::vector& GetOrCreateFeasibilityFilters(); DecisionBuilder* CreateSolutionFinalizer(); - void CreateFirstSolutionDecisionBuilders(); - DecisionBuilder* GetFirstSolutionDecisionBuilder() const; - IntVarFilteredDecisionBuilder* GetFilteredFirstSolutionDecisionBuilderOrNull() - const; - LocalSearchPhaseParameters* CreateLocalSearchParameters(); - DecisionBuilder* CreateLocalSearchDecisionBuilder(); - void SetupDecisionBuilders(); - void SetupMetaheuristics(); + void CreateFirstSolutionDecisionBuilders( + const RoutingSearchParameters& search_parameters); + DecisionBuilder* GetFirstSolutionDecisionBuilder( + const RoutingSearchParameters& search_parameters) const; + IntVarFilteredDecisionBuilder* GetFilteredFirstSolutionDecisionBuilderOrNull( + const RoutingSearchParameters& parameters) const; + LocalSearchPhaseParameters* CreateLocalSearchParameters( + const RoutingSearchParameters& search_parameters); + DecisionBuilder* CreateLocalSearchDecisionBuilder( + const RoutingSearchParameters& search_parameters); + void SetupDecisionBuilders(const RoutingSearchParameters& search_parameters); + void SetupMetaheuristics(const RoutingSearchParameters& search_parameters); void SetupAssignmentCollector(); void SetupTrace(); - void SetupSearchMonitors(); - bool UsesLightPropagation() const; + void SetupSearchMonitors(const RoutingSearchParameters& search_parameters); + bool UsesLightPropagation( + const RoutingSearchParameters& search_parameters) const; int64 GetArcCostForCostClassInternal(int64 i, int64 j, int64 cost_class); int GetVehicleStartClass(int64 start) const; @@ -1262,6 +1191,8 @@ class RoutingModel { std::unique_ptr > vehicle_start_class_callback_; // Cached callbacks hash_map cached_node_callbacks_; + hash_map + cached_state_dependent_callbacks_; // Disjunctions ITIVector disjunctions_; std::vector node_to_disjunction_; @@ -1285,10 +1216,8 @@ class RoutingModel { std::vector first_solution_decision_builders_; std::vector first_solution_filtered_decision_builders_; - RoutingStrategy first_solution_strategy_; Solver::IndexEvaluator2 first_solution_evaluator_; std::vector local_search_operators_; - RoutingMetaheuristic metaheuristic_; std::vector monitors_; SolutionCollector* collect_assignments_; DecisionBuilder* solve_db_; @@ -1308,15 +1237,13 @@ class RoutingModel { std::unique_ptr sweep_arranger_; #endif - int64 time_limit_ms_; - int64 lns_time_limit_ms_; SearchLimit* limit_; SearchLimit* ls_limit_; SearchLimit* lns_limit_; // Callbacks to be deleted hash_set owned_node_callbacks_; - hash_set owned_index_callbacks_; + hash_set owned_state_dependent_callbacks_; friend class RoutingDimension; @@ -1329,9 +1256,13 @@ class RoutingModel { // Quantities at a node are represented by "cumul" variables and the increase // or decrease of quantities between nodes are represented by "transit" // variables. These variables are linked as follows: -// if j == next(i), cumuls(j) = cumuls(i) + transits(i) + slacks(i) +// if j == next(i), +// cumuls(j) = cumuls(i) + transits(i) + slacks(i) + state_dependent_transits(i) // where slack is a positive slack variable (can represent waiting times for -// a time dimension). +// a time dimension), and state_dependent_transits is a non-purely functional +// version of transits_. Favour transits over state_dependent_transits when +// possible, because purely functional callbacks allow more optimisations and +// make the model faster and easier to solve. class RoutingDimension { public: // Returns the transition value for a given pair of nodes (as var index); @@ -1505,6 +1436,16 @@ class RoutingDimension { // Returns the cost coefficient of the soft lower bound of a cumul variable // for a variable index. If no soft lower bound has been set, 0 is returned. int64 GetCumulVarSoftLowerBoundCoefficientFromIndex(int64 index) const; + // Returns the parent in the dependency tree if any or nullptr otherwise. + const RoutingDimension* base_dimension() const { return base_dimension_; } + // It makes sense to use the function only for self-dependent dimension. + // For such dimensions the value of the slack of a node determines the + // transition cost of the next transit. Provided that + // 1. cumul[node] is fixed, + // 2. next[node] and next[next[node]] (if exists) are fixed, + // the value of slack[node] for which cumul[next[node]] + transit[next[node]] + // is minimized can be found in O(1) using this function. + int64 ShortestTransitionSlack(int64 node) const; // Returns the name of the dimension. const std::string& name() const { return name_; } @@ -1538,15 +1479,22 @@ class RoutingDimension { int64 coefficient; }; - RoutingDimension(RoutingModel* model, const std::string& name); + class SelfBased {}; + RoutingDimension(RoutingModel* model, const std::string& name, + const RoutingDimension* base_dimension); + RoutingDimension(RoutingModel* model, const std::string& name, SelfBased); void Initialize( RoutingModel::VehicleEvaluator* vehicle_capacity, int64 capacity, const std::vector& transit_evaluators, + const std::vector& + state_dependent_node_evaluators, int64 slack_max); void InitializeCumuls(RoutingModel::VehicleEvaluator* vehicle_capacity, int64 capacity); void InitializeTransits( const std::vector& transit_evaluators, + const std::vector& + state_dependent_transit_evaluators, int64 slack_max); // Sets up the cost variables related to cumul soft upper bounds. void SetupCumulVarSoftUpperBoundCosts(std::vector* cost_elements) const; @@ -1555,17 +1503,36 @@ class RoutingDimension { // Sets up the cost variables related to the global span and per-vehicle span // costs (only for the "slack" part of the latter). void SetupGlobalSpanCost(std::vector* cost_elements) const; - void SetupSlackCosts(std::vector* cost_elements) const; + void SetupSlackAndDependentTransitCosts(std::vector* cost_elements) const; + // Finalize the model of the dimension. + void CloseModel(bool use_light_propagation); std::vector cumuls_; + std::vector capacity_vars_; std::unique_ptr capacity_evaluator_; std::vector transits_; + std::vector fixed_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 vehicle_to_class_; + + // The transits of a dimension may depend on its cumuls or the cumuls of + // another dimension. There can be no cycles, except for self loops, a typical + // example for this is a time dimension. + const RoutingDimension* const base_dimension_; + + // "state_dependent_transit_evaluators_" does the indexing by vehicle, while + // "state_dependent_class_evaluators_" does the de-duplicated ownership. + std::vector + state_dependent_transit_evaluators_; + std::vector > + state_dependent_class_evaluators_; + std::vector state_dependent_vehicle_to_class_; + std::vector slacks_; + std::vector dependent_transits_; std::vector vehicle_span_upper_bounds_; int64 global_span_cost_coefficient_; std::vector vehicle_span_cost_coefficients_; @@ -2053,5 +2020,4 @@ RoutingLocalSearchFilter* MakeNodePrecedenceFilter( RoutingLocalSearchFilter* MakeVehicleVarFilter( const RoutingModel& routing_model); } // namespace operations_research - #endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_ diff --git a/src/constraint_solver/routing_enums.proto b/src/constraint_solver/routing_enums.proto new file mode 100644 index 0000000000..56589d4c63 --- /dev/null +++ b/src/constraint_solver/routing_enums.proto @@ -0,0 +1,111 @@ +// 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. + +// Enums used to define routing parameters. + +syntax = "proto3"; +option java_package = "com.google.ortools.constraintsolver"; +option java_multiple_files = true; +option csharp_namespace = "Google.OrTools.ConstraintSolver"; + +package operations_research; + +// First solution strategies, used as starting point of local search. +message FirstSolutionStrategy { + enum Value { + // Lets the solver detect which strategy to use according to the model being + // solved. + AUTOMATIC = 0; + + // --- Path addition heuristics --- + // Starting from a route "start" node, connect it to the node which produces + // the cheapest route segment, then extend the route by iterating on the + // last node added to the route. + PATH_CHEAPEST_ARC = 3; + // Same as PATH_CHEAPEST_ARC, but arcs are evaluated with a comparison-based + // selector which will favor the most constrained arc first. To assign a + // selector to the routing model, see + // RoutingModel::ArcIsMoreConstrainedThanArc() in routing.h for details. + PATH_MOST_CONSTRAINED_ARC = 4; + // Same as PATH_CHEAPEST_ARC, except that arc costs are evaluated using the + // function passed to RoutingModel::SetFirstSolutionEvaluator() + // (cf. routing.h). + EVALUATOR_STRATEGY = 5; + // Savings algorithm (Clarke & Wright). + // Reference: Clarke, G. & Wright, J.W.: + // "Scheduling of Vehicles from a Central Depot to a Number of Delivery + // Points", Operations Research, Vol. 12, 1964, pp. 568-581 + SAVINGS = 10; + // Sweep algorithm (Wren & Holliday). + // Reference: Anthony Wren & Alan Holliday: Computer Scheduling of Vehicles + // from One or More Depots to a Number of Delivery Points Operational + // Research Quarterly (1970-1977), + // Vol. 23, No. 3 (Sep., 1972), pp. 333-344 + SWEEP = 11; + + // --- Path insertion heuristics --- + // Make all nodes inactive. Only finds a solution if nodes are optional (are + // element of a disjunction constraint with a finite penalty cost). + ALL_UNPERFORMED = 6; + // Iteratively build a solution by inserting the cheapest node at its + // cheapest position; the cost of insertion is based on the global cost + // function of the routing model. As of 2/2012, only works on models with + // optional nodes (with finite penalty costs). + BEST_INSERTION = 7; + // Iteratively build a solution by inserting the cheapest node at its + // cheapest position; the cost of insertion is based on the the arc cost + // function. Is faster than BEST_INSERTION. + PARALLEL_CHEAPEST_INSERTION = 8; + // Iteratively build a solution by inserting each node at its cheapest + // position; the cost of insertion is based on the the arc cost function. + // Differs from PARALLEL_CHEAPEST_INSERTION by the node selected for + // insertion; here nodes are considered in their order of creation. Is + // faster than PARALLEL_CHEAPEST_INSERTION. + LOCAL_CHEAPEST_INSERTION = 9; + + // --- Variable-based heuristics --- + // Iteratively connect two nodes which produce the cheapest route segment. + GLOBAL_CHEAPEST_ARC = 1; + // Select the first node with an unbound successor and connect it to the + // node which produces the cheapest route segment. + LOCAL_CHEAPEST_ARC = 2; + // Select the first node with an unbound successor and connect it to the + // first available node. + // This is equivalent to the CHOOSE_FIRST_UNBOUND strategy combined with + // ASSIGN_MIN_VALUE (cf. constraint_solver.h). + FIRST_UNBOUND_MIN_VALUE = 12; + } +} + +// Local search metaheuristics used to guide the search. Apart from greedy +// descent, they will try to escape local minima. +message LocalSearchMetaheuristic { + enum Value { + // Lets the solver select the metaheuristic. + AUTOMATIC = 0; + + // Accepts improving (cost-reducing) local search neighbors until a local + // minimum is reached. + GREEDY_DESCENT = 1; + // Uses guided local search to escape local minima + // (cf. http://en.wikipedia.org/wiki/Guided_Local_Search); this is generally + // the most efficient metaheuristic for vehicle routing. + GUIDED_LOCAL_SEARCH = 2; + // Uses simulated annealing to escape local minima + // (cf. http://en.wikipedia.org/wiki/Simulated_annealing). + SIMULATED_ANNEALING = 3; + // Uses tabu search to escape local minima + // (cf. http://en.wikipedia.org/wiki/Tabu_search). + TABU_SEARCH = 4; + } +} diff --git a/src/constraint_solver/routing_flags.cc b/src/constraint_solver/routing_flags.cc new file mode 100644 index 0000000000..431db1f7f7 --- /dev/null +++ b/src/constraint_solver/routing_flags.cc @@ -0,0 +1,200 @@ +// 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. + +#include "constraint_solver/routing_flags.h" + +#include +#include + +#include "base/map_util.h" + +// --- Routing search flags --- + +// Neighborhood activation/deactivation +DEFINE_bool(routing_no_lns, false, + "Routing: forbids use of Large Neighborhood Search."); +DEFINE_bool(routing_no_fullpathlns, true, + "Routing: forbids use of Full-path Large Neighborhood Search."); +DEFINE_bool(routing_no_relocate, false, + "Routing: forbids use of Relocate neighborhood."); +DEFINE_bool(routing_no_relocate_neighbors, true, + "Routing: forbids use of RelocateNeighbors neighborhood."); +DEFINE_bool(routing_no_exchange, false, + "Routing: forbids use of Exchange neighborhood."); +DEFINE_bool(routing_no_cross, false, + "Routing: forbids use of Cross neighborhood."); +DEFINE_bool(routing_no_2opt, false, + "Routing: forbids use of 2Opt neighborhood."); +DEFINE_bool(routing_no_oropt, false, + "Routing: forbids use of OrOpt neighborhood."); +DEFINE_bool(routing_no_make_active, false, + "Routing: forbids use of MakeActive/SwapActive/MakeInactive " + "neighborhoods."); +DEFINE_bool(routing_no_lkh, false, "Routing: forbids use of LKH neighborhood."); +DEFINE_bool(routing_no_tsp, true, + "Routing: forbids use of TSPOpt neighborhood."); +DEFINE_bool(routing_no_tsplns, true, + "Routing: forbids use of TSPLNS neighborhood."); +DEFINE_bool(routing_use_chain_make_inactive, false, + "Routing: use chain version of MakeInactive neighborhood."); +DEFINE_bool(routing_use_extended_swap_active, false, + "Routing: use extended version of SwapActive neighborhood."); + +// Meta-heuristics +DEFINE_bool(routing_guided_local_search, false, "Routing: use GLS."); +DEFINE_double(routing_guided_local_search_lambda_coefficient, 0.1, + "Lambda coefficient in GLS."); +DEFINE_bool(routing_simulated_annealing, false, + "Routing: use simulated annealing."); +DEFINE_bool(routing_tabu_search, false, "Routing: use tabu search."); + +// Search limits +DEFINE_int64(routing_solution_limit, kint64max, + "Routing: number of solutions limit."); +DEFINE_int64(routing_time_limit, kint64max, "Routing: time limit in ms."); +DEFINE_int64(routing_lns_time_limit, 100, + "Routing: time limit in ms for LNS sub-decisionbuilder."); + +// Search control +DEFINE_string(routing_first_solution, "", + "Routing first solution heuristic. See SetupParametersFromFlags " + "in the code to get a full list."); +DEFINE_bool(routing_use_filtered_first_solutions, true, + "Use filtered version of first solution heuristics if available."); +DEFINE_bool(routing_dfs, false, "Routing: use a complete depth-first search."); +DEFINE_int64(routing_optimization_step, 1, "Optimization step."); + +// Propagation control +DEFINE_bool(routing_use_light_propagation, true, + "Use constraints with light propagation in routing model."); + +// Misc +DEFINE_bool(routing_fingerprint_arc_cost_evaluators, true, + "Compare arc-cost evaluators using the fingerprint of their " + "corresponding matrix instead of evaluator addresses."); + +// --- Routing model flags --- +DEFINE_bool(routing_use_homogeneous_costs, true, + "Routing: use homogeneous cost model when possible."); +DEFINE_bool(routing_gzip_compress_trail, false, + "Use gzip to compress the trail, zippy otherwise."); + +namespace operations_research { + +void SetFirstSolutionStrategyFromFlags(RoutingSearchParameters* parameters) { + CHECK(parameters != nullptr); + const std::map + first_solution_string_to_parameters = { + {"PathCheapestArc", FirstSolutionStrategy::PATH_CHEAPEST_ARC}, + {"PathMostConstrainedArc", + FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC}, + {"EvaluatorStrategy", FirstSolutionStrategy::EVALUATOR_STRATEGY}, + {"Savings", FirstSolutionStrategy::SAVINGS}, + {"Sweep", FirstSolutionStrategy::SWEEP}, + {"AllUnperformed", FirstSolutionStrategy::ALL_UNPERFORMED}, + {"BestInsertion", FirstSolutionStrategy::BEST_INSERTION}, + {"GlobalCheapestInsertion", + FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION}, + {"LocalCheapestInsertion", + FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION}, + {"GlobalCheapestArc", FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC}, + {"LocalCheapestArc", FirstSolutionStrategy::LOCAL_CHEAPEST_ARC}, + {"DefaultStrategy", FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE}, + {"", FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE}}; + FirstSolutionStrategy::Value strategy; + if (FindCopy(first_solution_string_to_parameters, + FLAGS_routing_first_solution, &strategy)) { + parameters->set_first_solution_strategy(strategy); + } + parameters->set_use_filtered_first_solution_strategy( + FLAGS_routing_use_filtered_first_solutions); +} + +void SetLocalSearchMetaheuristicFromFlags(RoutingSearchParameters* parameters) { + CHECK(parameters != nullptr); + if (FLAGS_routing_tabu_search) { + parameters->set_local_search_metaheuristic( + LocalSearchMetaheuristic::TABU_SEARCH); + } else if (FLAGS_routing_simulated_annealing) { + parameters->set_local_search_metaheuristic( + LocalSearchMetaheuristic::SIMULATED_ANNEALING); + } else if (FLAGS_routing_guided_local_search) { + parameters->set_local_search_metaheuristic( + LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH); + } + parameters->set_guided_local_search_lambda_coefficient( + FLAGS_routing_guided_local_search_lambda_coefficient); +} + +void AddLocalSearchNeighborhoodOperatorsFromFlags( + RoutingSearchParameters* parameters) { + CHECK(parameters != nullptr); + RoutingSearchParameters::LocalSearchNeighborhoodOperators* const + local_search_operators = parameters->mutable_local_search_operators(); + local_search_operators->set_use_relocate_pair(true); + local_search_operators->set_use_relocate(!FLAGS_routing_no_relocate); + local_search_operators->set_use_relocate_neighbors( + !FLAGS_routing_no_relocate_neighbors); + local_search_operators->set_use_exchange(!FLAGS_routing_no_exchange); + local_search_operators->set_use_cross(!FLAGS_routing_no_cross); + local_search_operators->set_use_two_opt(!FLAGS_routing_no_2opt); + local_search_operators->set_use_or_opt(!FLAGS_routing_no_oropt); + local_search_operators->set_use_lin_kernighan(!FLAGS_routing_no_lkh); + local_search_operators->set_use_tsp_opt(!FLAGS_routing_no_tsp); + local_search_operators->set_use_make_active(!FLAGS_routing_no_make_active); + local_search_operators->set_use_make_inactive( + !FLAGS_routing_use_chain_make_inactive && !FLAGS_routing_no_make_active); + local_search_operators->set_use_make_chain_inactive( + FLAGS_routing_use_chain_make_inactive && !FLAGS_routing_no_make_active); + local_search_operators->set_use_swap_active( + !FLAGS_routing_use_extended_swap_active && !FLAGS_routing_no_make_active); + local_search_operators->set_use_extended_swap_active( + FLAGS_routing_use_extended_swap_active && !FLAGS_routing_no_make_active); + local_search_operators->set_use_path_lns(!FLAGS_routing_no_lns); + local_search_operators->set_use_inactive_lns(!FLAGS_routing_no_lns); + local_search_operators->set_use_full_path_lns(!FLAGS_routing_no_fullpathlns); + local_search_operators->set_use_tsp_lns(!FLAGS_routing_no_tsplns); +} + +void SetSearchLimitsFromFlags(RoutingSearchParameters* parameters) { + CHECK(parameters != nullptr); + parameters->set_use_depth_first_search(FLAGS_routing_dfs); + parameters->set_optimization_step(FLAGS_routing_optimization_step); + parameters->set_solution_limit(FLAGS_routing_solution_limit); + parameters->set_time_limit_ms(FLAGS_routing_time_limit); + parameters->set_lns_time_limit_ms(FLAGS_routing_lns_time_limit); +} + +void SetMiscellaneousParametersFromFlags(RoutingSearchParameters* parameters) { + CHECK(parameters != nullptr); + parameters->set_use_light_propagation(FLAGS_routing_use_light_propagation); + parameters->set_fingerprint_arc_cost_evaluators( + FLAGS_routing_fingerprint_arc_cost_evaluators); +} + +RoutingSearchParameters BuildSearchParametersFromFlags() { + RoutingSearchParameters parameters; + SetFirstSolutionStrategyFromFlags(¶meters); + SetLocalSearchMetaheuristicFromFlags(¶meters); + AddLocalSearchNeighborhoodOperatorsFromFlags(¶meters); + SetSearchLimitsFromFlags(¶meters); + SetMiscellaneousParametersFromFlags(¶meters); + return parameters; +} + +RoutingModelParameters BuildModelParametersFromFlags() { + RoutingModelParameters parameters; + parameters.set_reduce_vehicle_cost_model(FLAGS_routing_use_homogeneous_costs); + return parameters; +} +} // namespace operations_research diff --git a/src/constraint_solver/routing_flags.h b/src/constraint_solver/routing_flags.h new file mode 100644 index 0000000000..b2efa8252e --- /dev/null +++ b/src/constraint_solver/routing_flags.h @@ -0,0 +1,77 @@ +// 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_CONSTRAINT_SOLVER_ROUTING_FLAGS_H_ +#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FLAGS_H_ + +#include + +#include "base/commandlineflags.h" +#include "constraint_solver/routing_parameters.pb.h" + +// --- Routing search flags --- + +// Neighborhood activation/deactivation +DECLARE_bool(routing_no_lns); +DECLARE_bool(routing_no_fullpathlns); +DECLARE_bool(routing_no_relocate); +DECLARE_bool(routing_no_relocate_neighbors); +DECLARE_bool(routing_no_exchange); +DECLARE_bool(routing_no_cross); +DECLARE_bool(routing_no_2opt); +DECLARE_bool(routing_no_oropt); +DECLARE_bool(routing_no_make_active); +DECLARE_bool(routing_no_lkh); +DECLARE_bool(routing_no_tsp); +DECLARE_bool(routing_no_tsplns); +DECLARE_bool(routing_use_chain_make_inactive); +DECLARE_bool(routing_use_extended_swap_active); + +// Meta-heuristics +DECLARE_bool(routing_guided_local_search); +DECLARE_double(routing_guided_local_search_lambda_coefficient); +DECLARE_bool(routing_simulated_annealing); +DECLARE_bool(routing_tabu_search); + +// Search limits +DECLARE_int64(routing_solution_limit); +DECLARE_int64(routing_time_limit); +DECLARE_int64(routing_lns_time_limit); + +// Search control +DECLARE_string(routing_first_solution); +DECLARE_bool(routing_use_filtered_first_solutions); +DECLARE_bool(routing_dfs); +DECLARE_int64(routing_optimization_step); + +// Propagation control +DECLARE_bool(routing_use_light_propagation); + +// Misc +DECLARE_bool(routing_fingerprint_arc_cost_evaluators); + +// --- Routing model flags --- +DECLARE_bool(routing_use_homogeneous_costs); +DECLARE_bool(routing_gzip_compress_trail); + +namespace operations_research { + +// Builds routing search parameters from flags. +RoutingModelParameters BuildModelParametersFromFlags(); + +// Builds routing search parameters from flags. +RoutingSearchParameters BuildSearchParametersFromFlags(); + +} // namespace operations_research + +#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FLAGS_H_ diff --git a/src/constraint_solver/routing_parameters.proto b/src/constraint_solver/routing_parameters.proto new file mode 100644 index 0000000000..1e2ee6f01f --- /dev/null +++ b/src/constraint_solver/routing_parameters.proto @@ -0,0 +1,265 @@ +// 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. + +// Protocol buffer used to parametrize the routing library, in particular the +// search parameters such as first solution heuristics and local search +// neighborhoods. + +syntax = "proto3"; +option java_package = "com.google.ortools.constraintsolver"; +option java_multiple_files = true; +option csharp_namespace = "Google.OrTools.ConstraintSolver"; + +import "constraint_solver/routing_enums.proto"; + +package operations_research; + +// Parameters defining the search used to solve vehicle routing problems. +message RoutingSearchParameters { + // First solution strategies, used as starting point of local search. + FirstSolutionStrategy.Value first_solution_strategy = 1; + // These are advanced first solutions strategy settings which should not be + // modified unless you know what you are doing. + // Use filtered version of first solution strategy if available. + bool use_filtered_first_solution_strategy = 2; + + // Local search neighborhood operators used to build a solutions neighborhood. + message LocalSearchNeighborhoodOperators { + // --- Intra-route operators --- + // Operator which moves a single node to another position. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5 + // (where (1, 5) are first and last nodes of the path and can therefore not + // be moved): + // 1 -> 3 -> [2] -> 4 -> 5 + // 1 -> 3 -> 4 -> [2] -> 5 + // 1 -> 2 -> 4 -> [3] -> 5 + // 1 -> [4] -> 2 -> 3 -> 5 + bool use_relocate = 1; + // Operator which moves a pair of pickup and delivery nodes to another + // position where the first node of the pair must be before the second node + // on the same path. + // Possible neighbors for the path 1 -> A -> B -> 2 -> 3 (where (1, 3) are + // first and last nodes of the path and can therefore not be moved, and + // (A, B) is a pair of nodes): + // 1 -> [A] -> 2 -> [B] -> 3 + // 1 -> 2 -> [A] -> [B] -> 3 + bool use_relocate_pair = 2; + // Relocate neighborhood which moves chains of neighbors. + // The operator starts by relocating a node n after a node m, then continues + // moving nodes which were after n as long as the "cost" added is less than + // the "cost" of the arc (m, n). If the new chain doesn't respect the domain + // of next variables, it will try reordering the nodes until it finds a + // valid path. + // Possible neighbors for path 1 -> A -> B -> C -> D -> E -> 2 (where (1, 2) + // are first and last nodes of the path and can therefore not be moved, A + // must be performed before B, and A, D and E are located at the same + // place): + // 1 -> A -> C -> [B] -> D -> E -> 2 + // 1 -> A -> C -> D -> [B] -> E -> 2 + // 1 -> A -> C -> D -> E -> [B] -> 2 + // 1 -> A -> B -> D -> [C] -> E -> 2 + // 1 -> A -> B -> D -> E -> [C] -> 2 + // 1 -> A -> [D] -> [E] -> B -> C -> 2 + // 1 -> A -> B -> [D] -> [E] -> C -> 2 + // 1 -> A -> [E] -> B -> C -> D -> 2 + // 1 -> A -> B -> [E] -> C -> D -> 2 + // 1 -> A -> B -> C -> [E] -> D -> 2 + // This operator is extremelly useful to move chains of nodes which are + // located at the same place (for instance nodes part of a same stop). + bool use_relocate_neighbors = 3; + // Operator which exchanges the positions of two nodes. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5 + // (where (1, 5) are first and last nodes of the path and can therefore not + // be moved): + // 1 -> [3] -> [2] -> 4 -> 5 + // 1 -> [4] -> 3 -> [2] -> 5 + // 1 -> 2 -> [4] -> [3] -> 5 + bool use_exchange = 4; + // Operator which cross exchanges the starting chains of 2 paths, including + // exchanging the whole paths. + // First and last nodes are not moved. + // Possible neighbors for the paths 1 -> 2 -> 3 -> 4 -> 5 and 6 -> 7 -> 8 + // (where (1, 5) and (6, 8) are first and last nodes of the paths and can + // therefore not be moved): + // 1 -> [7] -> 3 -> 4 -> 5 6 -> [2] -> 8 + // 1 -> [7] -> 4 -> 5 6 -> [2 -> 3] -> 8 + // 1 -> [7] -> 5 6 -> [2 -> 3 -> 4] -> 8 + bool use_cross = 5; + // Not implemented as of 10/2015. + bool use_cross_exchange = 6; + // --- Inter-route operators --- + // Operator which reverves a sub-chain of a path. It is called TwoOpt + // because it breaks two arcs on the path; resulting paths are called + // two-optimal. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5 + // (where (1, 5) are first and last nodes of the path and can therefore not + // be moved): + // 1 -> [3 -> 2] -> 4 -> 5 + // 1 -> [4 -> 3 -> 2] -> 5 + // 1 -> 2 -> [4 -> 3] -> 5 + bool use_two_opt = 7; + // Operator which moves sub-chains of a path of length 1, 2 and 3 to another + // position in the same path. + // When the length of the sub-chain is 1, the operator simply moves a node + // to another position. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5, for a sub-chain + // length of 2 (where (1, 5) are first and last nodes of the path and can + // therefore not be moved): + // 1 -> 4 -> [2 -> 3] -> 5 + // 1 -> [3 -> 4] -> 2 -> 5 + // The OR_OPT operator is a limited version of 3-Opt (breaks 3 arcs on a + // path). + bool use_or_opt = 8; + // Lin–Kernighan operator. + // While the accumulated local gain is positive, performs a 2-OPT or a 3-OPT + // move followed by a series of 2-OPT moves. Returns a neighbor for which + // the global gain is positive. + bool use_lin_kernighan = 9; + // Sliding TSP operator. + // Uses an exact dynamic programming algorithm to solve the TSP + // corresponding to path sub-chains. + // For a subchain 1 -> 2 -> 3 -> 4 -> 5 -> 6, solves the TSP on + // nodes A, 2, 3, 4, 5, where A is a merger of nodes 1 and 6 such that + // cost(A,i) = cost(1,i) and cost(i,A) = cost(i,6). + bool use_tsp_opt = 10; + // --- Operators on unactive nodes --- + // Operator which inserts an inactive node into a path. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive + // (where 1 and 4 are first and last nodes of the path) are: + // 1 -> [5] -> 2 -> 3 -> 4 + // 1 -> 2 -> [5] -> 3 -> 4 + // 1 -> 2 -> 3 -> [5] -> 4 + bool use_make_active = 11; + // Operator which makes path nodes inactive. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first + // and last nodes of the path) are: + // 1 -> 3 -> 4 with 2 inactive + // 1 -> 2 -> 4 with 3 inactive + bool use_make_inactive = 12; + // Operator which makes a "chain" of path nodes inactive. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first + // and last nodes of the path) are: + // 1 -> 3 -> 4 with 2 inactive + // 1 -> 2 -> 4 with 3 inactive + // 1 -> 4 with 2 and 3 inactive + bool use_make_chain_inactive = 13; + // Operator which replaces an active node by an inactive one. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive + // (where 1 and 4 are first and last nodes of the path) are: + // 1 -> [5] -> 3 -> 4 with 2 inactive + // 1 -> 2 -> [5] -> 4 with 3 inactive + bool use_swap_active = 14; + // Operator which makes an inactive node active and an active one inactive. + // It is similar to SwapActiveOperator excepts that it tries to insert the + // inactive node in all possible positions instead of just the position of + // the node made inactive. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive + // (where 1 and 4 are first and last nodes of the path) are: + // 1 -> [5] -> 3 -> 4 with 2 inactive + // 1 -> 3 -> [5] -> 4 with 2 inactive + // 1 -> [5] -> 2 -> 4 with 3 inactive + // 1 -> 2 -> [5] -> 4 with 3 inactive + bool use_extended_swap_active = 15; + // Operator which makes an inactive node active and an active pair of nodes + // inactive OR makes an inactive pair of nodes active and an active node + // inactive. + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive + // (where 1 and 4 are first and last nodes of the path and (2,3) is a pair + // of nodes) are: + // 1 -> [5] -> 4 with (2,3) inactive + // Possible neighbors for the path 1 -> 2 -> 3 with (4,5) inactive + // (where 1 and 3 are first and last nodes of the path and (4,5) is a pair + // of nodes) are: + // 1 -> [4] -> [5] -> 3 with 2 inactive + bool use_node_pair_swap_active = 20; + // --- Large neighborhood search operators --- + // Operator which relaxes two sub-chains of three consecutive arcs each. + // Each sub-chain is defined by a start node and the next three arcs. Those + // six arcs are relaxed to build a new neighbor. + // PATH_LNS explores all possible pairs of starting nodes and so defines + // n^2 neighbors, n being the number of nodes. + // Note that the two sub-chains can be part of the same path; they even may + // overlap. + bool use_path_lns = 16; + // Operator which relaxes one entire path and all unactive nodes. + bool use_full_path_lns = 17; + // TSP-base LNS. + // Randomly merges consecutive nodes until n "meta"-nodes remain and solves + // the corresponding TSP. + // This defines an "unlimited" neighborhood which must be stopped by search + // limits. To force diversification, the operator iteratively forces each + // node to serve as base of a meta-node. + bool use_tsp_lns = 18; + // Operator which relaxes all inactive nodes and one sub-chain of six + // consecutive arcs. That way the path can be improved by inserting inactive + // nodes or swaping arcs. + bool use_inactive_lns = 19; + } + LocalSearchNeighborhoodOperators local_search_operators = 3; + + // Local search metaheuristics used to guide the search. + LocalSearchMetaheuristic.Value local_search_metaheuristic = 4; + // These are advanced settings which should not be modified unless you know + // what you are doing. + // Lambda coefficient used to penalize arc costs when GUIDED_LOCAL_SEARCH is + // used. + double guided_local_search_lambda_coefficient = 5; + + // --- Search control --- + // If true, the solver should use depth-first search rather than local search + // to solve the problem. + bool use_depth_first_search = 6; + // Minimum step by which the solution must be improved in local search. + int64 optimization_step = 7; + // -- Search limits -- + // Limit to the number of solutions generated during the search. + int64 solution_limit = 8; + // Limit in milliseconds to the time spent in the search. + int64 time_limit_ms = 9; + // Limit in milliseconds to the time spent in the completion search for each + // local search neighbor. + int64 lns_time_limit_ms = 10; + + // --- Propagation control --- + // These are advanced settings which should not be modified unless you know + // what you are doing. + // Use constraints with light propagation in routing model. Extra propagation + // is only necessary when using depth-first search or for models which + // require strong propagation to finalize the value of secondary variables. + // Changing this setting to true will slow down the search in most cases and + // increase memory consumption in all cases. + bool use_light_propagation = 11; + + // --- Miscellaneous --- + // These are advanced settings which should not be modified unless you know + // what you are doing. + // If true, arc cost evaluators will be fingerprinted. The fingerprint will + // be used when computing vehicle cost equivalent classes, otherwise the + // address of the evaluator will be used. + bool fingerprint_arc_cost_evaluators = 12; +} + +// Parameters which have to be set when creating a RoutingModel. +message RoutingModelParameters { + // Advanced settings. + // If set to true reduction of the underlying constraint model will be + // attempted when all vehicles have exactly the same cost structure. This can + // result in significant speedups. + bool reduce_vehicle_cost_model = 1; + // Algorithm to use to compress the constraint solver trail. + enum TrailCompression { + NONE = 0; + ZLIB = 1; + } + TrailCompression trail_compression = 2; +} diff --git a/src/constraint_solver/routing_search.cc b/src/constraint_solver/routing_search.cc index e4fc7ad17b..93dde074bf 100644 --- a/src/constraint_solver/routing_search.cc +++ b/src/constraint_solver/routing_search.cc @@ -16,8 +16,10 @@ // and local search filters. // TODO(user): Move all existing routing search code here. +#include #include #include +#include #include "base/small_map.h" #include "base/small_ordered_set.h" #include "constraint_solver/routing.h" @@ -1388,9 +1390,7 @@ bool RoutingFilteredDecisionBuilder::InitializeRoutes() { void RoutingFilteredDecisionBuilder::MakeDisjunctionNodesUnperformed( int64 node) { - std::vector alternates; - model()->GetDisjunctionIndicesFromIndex(node, &alternates); - for (const int alternate : alternates) { + for (const int alternate : model()->GetDisjunctionIndicesFromIndex(node)) { if (node != alternate) { SetValue(alternate, alternate); } @@ -1758,7 +1758,8 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePickupPositions( // the entries which are being kept and must be updated. #if defined(_MSC_VER) hash_set, - PairPairInt64Hasher> existing_insertions; + PairPairInt64Hasher> + existing_insertions; #else hash_set> existing_insertions; @@ -1864,7 +1865,8 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdateDeliveryPositions( // the entries which are being kept and must be updated. #if defined(_MSC_VER) hash_set, - PairPairInt64Hasher> existing_insertions; + PairPairInt64Hasher> + existing_insertions; #else hash_set> existing_insertions; @@ -2515,4 +2517,266 @@ SavingsFilteredDecisionBuilder::ComputeSavings() const { std::sort(savings.begin(), savings.end()); return savings; } + +namespace { +// The description is in routing.h:MakeGuidedSlackFinalizer +class GuidedSlackFinalizer : public DecisionBuilder { + public: + GuidedSlackFinalizer(const RoutingDimension* dimension, RoutingModel* model, + std::function initializer); + Decision* Next(Solver* solver) override; + + private: + int64 SelectValue(int64 index); + int64 ChooseVariable(); + + const RoutingDimension* const dimension_; + RoutingModel* const model_; + const std::function initializer_; + RevArray is_initialized_; + std::vector initial_values_; + Rev current_index_; + Rev current_route_; + RevArray last_delta_used_; + + DISALLOW_COPY_AND_ASSIGN(GuidedSlackFinalizer); +}; + +GuidedSlackFinalizer::GuidedSlackFinalizer( + const RoutingDimension* dimension, RoutingModel* model, + std::function initializer) + : dimension_(CHECK_NOTNULL(dimension)), + model_(CHECK_NOTNULL(model)), + initializer_(std::move(initializer)), + is_initialized_(dimension->slacks().size(), false), + initial_values_(dimension->slacks().size(), kint64min), + current_index_(model_->Start(0)), + current_route_(0), + last_delta_used_(dimension->slacks().size(), 0) {} + +Decision* GuidedSlackFinalizer::Next(Solver* solver) { + CHECK_EQ(solver, model_->solver()); + const int node_idx = ChooseVariable(); + CHECK(node_idx == -1 || + (node_idx >= 0 && node_idx < dimension_->slacks().size())); + if (node_idx != -1) { + if (!is_initialized_[node_idx]) { + initial_values_[node_idx] = initializer_(node_idx); + is_initialized_.SetValue(solver, node_idx, true); + } + const int64 value = SelectValue(node_idx); + IntVar* const slack_variable = dimension_->SlackVar(node_idx); + return solver->MakeAssignVariableValue(slack_variable, value); + } + return nullptr; +} + +int64 GuidedSlackFinalizer::SelectValue(int64 index) { + const IntVar* const slack_variable = dimension_->SlackVar(index); + const int64 center = initial_values_[index]; + const int64 max_delta = + std::max(center - slack_variable->Min(), slack_variable->Max() - center) + + 1; + int64 delta = last_delta_used_[index]; + + // The sequence of deltas is 0, 1, -1, 2, -2 ... + // Only the values inside the domain of variable are returned. + while (std::abs(delta) < max_delta && + !slack_variable->Contains(center + delta)) { + if (delta > 0) { + delta = -delta; + } else { + delta = -delta + 1; + } + } + last_delta_used_.SetValue(model_->solver(), index, delta); + return center + delta; +} + +int64 GuidedSlackFinalizer::ChooseVariable() { + int64 int_current_node = current_index_.Value(); + int64 int_current_route = current_route_.Value(); + + while (int_current_route < model_->vehicles()) { + while (!model_->IsEnd(int_current_node) && + dimension_->SlackVar(int_current_node)->Bound()) { + int_current_node = model_->NextVar(int_current_node)->Value(); + } + if (!model_->IsEnd(int_current_node)) { + break; + } + int_current_route += 1; + if (int_current_route < model_->vehicles()) { + int_current_node = model_->Start(int_current_route); + } + } + + CHECK(int_current_route == model_->vehicles() || + !dimension_->SlackVar(int_current_node)->Bound()); + current_index_.SetValue(model_->solver(), int_current_node); + current_route_.SetValue(model_->solver(), int_current_route); + if (int_current_route < model_->vehicles()) { + return int_current_node; + } else { + return -1; + } +} +} // namespace + +DecisionBuilder* RoutingModel::MakeGuidedSlackFinalizer( + const RoutingDimension* dimension, + std::function initializer) { + return solver_->RevAlloc( + new GuidedSlackFinalizer(dimension, this, std::move(initializer))); +} + +int64 RoutingDimension::ShortestTransitionSlack(int64 node) const { + CHECK_EQ(base_dimension_, this); + CHECK(!model_->IsEnd(node)); + // Recall that the model is cumul[i+1] = cumul[i] + transit[i] + slack[i]. Our + // aim is to find a value for slack[i] such that cumul[i+1] + transit[i+1] is + // minimized. + const int64 next = model_->NextVar(node)->Value(); + if (model_->IsEnd(next)) { + return SlackVar(node)->Min(); + } + const int64 next_next = model_->NextVar(next)->Value(); + const int64 serving_vehicle = model_->VehicleVar(node)->Value(); + CHECK_EQ(serving_vehicle, model_->VehicleVar(next)->Value()); + const RoutingModel::StateDependentTransit transit_from_next = + state_dependent_transit_evaluators_[serving_vehicle]->Run(next, + next_next); + // We have that transit[i+1] is a function of cumul[i+1]. + const int64 next_cumul_min = CumulVar(next)->Min(); + const int64 next_cumul_max = CumulVar(next)->Max(); + const int64 optimal_next_cumul = + transit_from_next.transit_plus_identity->RangeMinArgument( + next_cumul_min, next_cumul_max + 1); + // A few checks to make sure we're on the same page. + DCHECK_LE(next_cumul_min, optimal_next_cumul); + DCHECK_LE(optimal_next_cumul, next_cumul_max); + // optimal_next_cumul = cumul + transit + optimal_slack, so + // optimal_slack = optimal_next_cumul - cumul - transit. + // In the current implementation TransitVar(i) = transit[i] + slack[i], so we + // have to find the transit from the evaluators. + const int64 current_cumul = CumulVar(node)->Value(); + const int64 current_state_independent_transit = + transit_evaluators_[serving_vehicle]->Run(node, next); + const int64 current_state_dependent_transit = + state_dependent_transit_evaluators_[serving_vehicle] + ->Run(node, next) + .transit->Query(current_cumul); + const int64 optimal_slack = optimal_next_cumul - current_cumul - + current_state_independent_transit - + current_state_dependent_transit; + CHECK_LE(SlackVar(node)->Min(), optimal_slack); + CHECK_LE(optimal_slack, SlackVar(node)->Max()); + return optimal_slack; +} + +namespace { +class GreedyDescentLSOperator : public LocalSearchOperator { + public: + explicit GreedyDescentLSOperator(std::vector variables); + + bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; + void Start(const Assignment* assignment) override; + + private: + int64 FindMaxDistanceToDomain(const Assignment* assignment); + + const std::vector variables_; + const Assignment* center_; + int64 current_step_; + // The deltas are returned in this order: + // (current_step_, 0, ... 0), (-current_step_, 0, ... 0), + // (0, current_step_, ... 0), (0, -current_step_, ... 0), + // ... + // (0, ... 0, current_step_), (0, ... 0, -current_step_). + // current_direction_ keeps track what was the last returned delta. + int64 current_direction_; + + DISALLOW_COPY_AND_ASSIGN(GreedyDescentLSOperator); +}; + +GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector variables) + : variables_(std::move(variables)), + center_(nullptr), + current_step_(0), + current_direction_(0) {} + +bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment* delta, + Assignment* /*deltadelta*/) { + static const int64 sings[] = {1, -1}; + for (; 1 <= current_step_; current_step_ /= 2) { + for (; current_direction_ < 2 * variables_.size();) { + const int64 variable_idx = current_direction_ / 2; + IntVar* const variable = variables_[variable_idx]; + const int64 sign_index = current_direction_ % 2; + const int64 sign = sings[sign_index]; + const int64 offset = sign * current_step_; + const int64 new_value = center_->Value(variable) + offset; + ++current_direction_; + if (variable->Contains(new_value)) { + delta->Add(variable); + delta->SetValue(variable, new_value); + return true; + } + } + current_direction_ = 0; + } + return false; +} + +void GreedyDescentLSOperator::Start(const Assignment* assignment) { + CHECK(assignment != nullptr); + current_step_ = FindMaxDistanceToDomain(assignment); + center_ = assignment; +} + +int64 GreedyDescentLSOperator::FindMaxDistanceToDomain( + const Assignment* assignment) { + int64 result = kint64min; + for (const IntVar* const var : variables_) { + result = std::max(result, std::abs(var->Max() - assignment->Value(var))); + result = std::max(result, std::abs(var->Min() - assignment->Value(var))); + } + return result; +} +} // namespace + +std::unique_ptr RoutingModel::MakeGreedyDescentLSOperator( + std::vector variables) { + return std::unique_ptr( + new GreedyDescentLSOperator(std::move(variables))); +} + +DecisionBuilder* RoutingModel::MakeSelfDependentDimensionFinalizer( + const RoutingDimension* dimension) { + CHECK(dimension != nullptr); + CHECK(dimension->base_dimension() == dimension); + std::function slack_guide = [dimension](int64 index) { + return dimension->ShortestTransitionSlack(index); + }; + DecisionBuilder* const guided_finalizer = + MakeGuidedSlackFinalizer(dimension, slack_guide); + DecisionBuilder* const slacks_finalizer = + solver_->MakeSolveOnce(guided_finalizer); + std::vector start_cumuls(vehicles_, nullptr); + for (int64 vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) { + start_cumuls[vehicle_idx] = dimension->CumulVar(starts_[vehicle_idx]); + } + LocalSearchOperator* const hill_climber = + solver_->RevAlloc(new GreedyDescentLSOperator(start_cumuls)); + LocalSearchPhaseParameters* const parameters = + solver_->MakeLocalSearchPhaseParameters(hill_climber, slacks_finalizer); + Assignment* const first_solution = solver_->MakeAssignment(); + first_solution->Add(start_cumuls); + for (IntVar* const cumul : start_cumuls) { + first_solution->SetValue(cumul, cumul->Min()); + } + DecisionBuilder* const finalizer = + solver_->MakeLocalSearchPhase(first_solution, parameters); + return finalizer; +} } // namespace operations_research diff --git a/src/constraint_solver/sched_constraints.cc b/src/constraint_solver/sched_constraints.cc index 14e053fbd4..8298efac11 100644 --- a/src/constraint_solver/sched_constraints.cc +++ b/src/constraint_solver/sched_constraints.cc @@ -47,7 +47,7 @@ class TreeArrayConstraint : public Constraint { : Constraint(solver), vars_(vars), target_var_(target_var), - block_size_(solver->parameters().array_split_size) { + block_size_(solver->parameters().array_split_size()) { std::vector lengths; lengths.push_back(vars_.size()); while (lengths.back() > 1) { diff --git a/src/constraint_solver/search.cc b/src/constraint_solver/search.cc index 30b8a260ec..cf92ce22c3 100644 --- a/src/constraint_solver/search.cc +++ b/src/constraint_solver/search.cc @@ -3782,12 +3782,23 @@ SearchLimit* Solver::MakeLimit(int64 time, int64 branches, int64 failures, smart_time_check, cumulative)); } -SearchLimit* Solver::MakeLimit(const SearchLimitProto& proto) { +SearchLimit* Solver::MakeLimit(const SearchLimitParameters& proto) { return MakeLimit(proto.time(), proto.branches(), proto.failures(), proto.solutions(), proto.smart_time_check(), proto.cumulative()); } +SearchLimitParameters Solver::MakeDefaultSearchLimitParameters() const { + SearchLimitParameters proto; + proto.set_time(kint64max); + proto.set_branches(kint64max); + proto.set_failures(kint64max); + proto.set_solutions(kint64max); + proto.set_smart_time_check(false); + proto.set_cumulative(false); + return proto; +} + // A limit whose Check function is the OR of two underlying limits. namespace { class ORLimit : public SearchLimit { diff --git a/src/constraint_solver/search_limit.proto b/src/constraint_solver/search_limit.proto index 985f0d464d..5fdcee99c3 100644 --- a/src/constraint_solver/search_limit.proto +++ b/src/constraint_solver/search_limit.proto @@ -14,18 +14,21 @@ // // This file contains a protocol buffer definition for search limits. -syntax = "proto2"; - +syntax = "proto3"; +option java_package = "com.google.ortools.constraintsolver"; +option java_multiple_files = true; +option java_outer_classname = "SearchLimitProtobuf"; +option csharp_namespace = "Google.OrTools.ConstraintSolver"; package operations_research; // A search limit // The default values for int64 fields is the maxima value, i.e., 2^63-1 -message SearchLimitProto { - optional int64 time = 1 [default = 9223372036854775807]; - optional int64 branches = 2 [default = 9223372036854775807]; - optional int64 failures = 3 [default = 9223372036854775807]; - optional int64 solutions = 4 [default = 9223372036854775807]; - optional bool smart_time_check = 5 [default = false]; - optional bool cumulative = 6 [default = false]; +message SearchLimitParameters { + int64 time = 1; + int64 branches = 2; + int64 failures = 3; + int64 solutions = 4; + bool smart_time_check = 5; + bool cumulative = 6; }; diff --git a/src/constraint_solver/solver_parameters.proto b/src/constraint_solver/solver_parameters.proto new file mode 100644 index 0000000000..6ab178da53 --- /dev/null +++ b/src/constraint_solver/solver_parameters.proto @@ -0,0 +1,95 @@ +// 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 contains protocol buffers for all parameters of the CP solver. + +syntax = "proto3"; +option java_package = "com.google.ortools.constraintsolver"; +option java_multiple_files = true; +option csharp_namespace = "Google.OrTools.ConstraintSolver"; + +package operations_research; + +// Solver parameters. +message ConstraintSolverParameters { + // + // Internal parameters of the solver. + // + enum TrailCompression { + NO_COMPRESSION = 0; + COMPRESS_WITH_ZLIB = 1; + }; + + // This parameter indicates if the solver should compress the trail + // during the search. No compression means that the solver will be faster, + // but will use more memory. + TrailCompression compress_trail = 1; + + // This parameter indicates the default size of a block of the trail. + // Compression applies at the block level. + int32 trail_block_size = 2; + + // When a sum/min/max operation is applied on a large array, this + // array is recursively split into blocks of size 'array_split_size'. + int32 array_split_size = 3; + + // + // Control naming of the variables.. + // + + // This parameters indicates if the solver should store the names of + // the objets it manages. + bool store_names = 4; + + // Create names for cast variables. + bool name_cast_variables = 5; + + // Should anonymous variables be given a name. + bool name_all_variables = 6; + + // + // Control monitoring of the solver and the model. + // + + // Activate propagation profiling. + bool profile_propagation = 7; + + // Export profiling info to file. + string profile_file = 8; + + // Activate propagate tracing. + bool trace_propagation = 9; + + // Trace search. + bool trace_search = 10; + + // Print the model before solving. + bool print_model = 11; + + // Print model statistics before solving. + bool print_model_stats = 12; + + // Print added constraints. + bool print_added_constraints = 13; + + // Export model to file. + string export_file = 14; + + // + // Control search. + // + + // Disable solving. + + bool disable_solve = 15; +}; diff --git a/src/linear_solver/python/linear_solver.swig b/src/linear_solver/python/linear_solver.swig index ac6137320c..a39a97cf6f 100644 --- a/src/linear_solver/python/linear_solver.swig +++ b/src/linear_solver/python/linear_solver.swig @@ -40,7 +40,6 @@ class MPSolutionResponse; } // namespace operations_research %{ -#include "linear_solver/linear_solver.pb.h" #include "linear_solver/linear_solver.h" %} @@ -48,12 +47,15 @@ namespace operations_research { %pythoncode { import numbers +from ortools.linear_solver.linear_solver_natural_api import OFFSET_KEY +from ortools.linear_solver.linear_solver_natural_api import inf from ortools.linear_solver.linear_solver_natural_api import LinearExpr from ortools.linear_solver.linear_solver_natural_api import ProductCst from ortools.linear_solver.linear_solver_natural_api import Sum from ortools.linear_solver.linear_solver_natural_api import SumArray from ortools.linear_solver.linear_solver_natural_api import SumCst from ortools.linear_solver.linear_solver_natural_api import LinearConstraint +from ortools.linear_solver.linear_solver_natural_api import VariableExpr } // %pythoncode %extend MPVariable { @@ -64,101 +66,9 @@ from ortools.linear_solver.linear_solver_natural_api import LinearConstraint return $self->name(); } - // Note(user): the lines below are a pure duplication of code from - // ../linear_solver_natural_api.py. Unfortunately it wasn't easy to fix. In - // particular, SWIG's %define won't work because they get prepended with a - // C-style comment "/*...*/" upon expansion, which breaks the python code. %pythoncode { - def IsConstant(self, expr): - return isinstance(expr, numbers.Number) - - def __add__(self, expr): - if self.IsConstant(expr): - return SumCst(self, expr) - else: - return Sum(self, expr) - - def __radd__(self, cst): - if self.IsConstant(cst): - return SumCst(self, cst) - else: - raise TypeError - - def __sub__(self, expr): - if self.IsConstant(expr): - return SumCst(self, -expr) - else: - return Sum(self, ProductCst(expr, -1)) - - def __rsub__(self, cst): - if self.IsConstant(cst): - return SumCst(ProductCst(self, -1), cst) - else: - raise TypeError - - def __mul__(self, cst): - if self.IsConstant(cst): - return ProductCst(self, cst) - else: - raise TypeError - - def __rmul__(self, cst): - if self.IsConstant(cst): - return ProductCst(self, cst) - else: - raise TypeError - - def __div__(self, cst): - if self.IsConstant(cst): - if cst == 0.0: - raise ZeroDivisionError - else: - return ProductCst(self, 1.0 / cst) - else: - raise TypeError - - def __truediv__(self, cst): - if self.IsConstant(cst): - if cst == 0.0: - raise ZeroDivisionError - else: - return ProductCst(self, 1.0 / cst) - else: - raise TypeError - - def __neg__(self): - return ProductCst(self, -1) - - def __eq__(self, arg): - if self.IsConstant(arg): - return LinearConstraint(self, arg, arg) - else: - return LinearConstraint(Sum(self, ProductCst(arg, -1)), 0.0, 0.0) - - def __hash__(self): - return object.__hash__(self) - - def __ge__(self, arg): - if self.IsConstant(arg): - return LinearConstraint(self, arg, 1e308) - else: - return LinearConstraint(Sum(self, ProductCst(arg, -1)), 0.0, 1e308) - - def __le__(self, arg): - if self.IsConstant(arg): - return LinearConstraint(self, -1e308, arg) - else: - return LinearConstraint(Sum(self, ProductCst(arg, -1)), -1e308, 0.0) - - def Visit(self, coeffs): - return self.DoVisit(coeffs, 1.0) - - def DoVisit(self, coeffs, multiplier): - if self in coeffs: - coeffs[self] += multiplier - else: - coeffs[self] = multiplier - return 0.0 + def __getattr__(self, name): + return getattr(VariableExpr(self), name) } // %pythoncode } @@ -401,3 +311,11 @@ from ortools.linear_solver.linear_solver_natural_api import LinearConstraint %include "linear_solver/linear_solver.h" %unignoreall + +%pythoncode { +def setup_variable_operator(opname): + setattr(Variable, opname, + lambda self, *args: getattr(VariableExpr(self), opname)(*args)) +for opname in LinearExpr.SUPPORTED_OPERATOR_METHODS: + setup_variable_operator(opname) +} // %pythoncode diff --git a/src/ortools/linear_solver/linear_solver_natural_api.py b/src/ortools/linear_solver/linear_solver_natural_api.py index 959002b591..a369e8f1ff 100644 --- a/src/ortools/linear_solver/linear_solver_natural_api.py +++ b/src/ortools/linear_solver/linear_solver_natural_api.py @@ -19,13 +19,41 @@ For examples leveraging the code defined here, see ./pywraplp_test.py and """ import numbers -import types # The classes below allow linear expressions to be expressed naturally with the # usual arithmetic operators +-*/ and with constant numbers, which makes the # python API very intuitive. See the top-level comment for examples. +inf = float('inf') + + +class _FakeMPVariableRepresentingTheConstantOffset(object): + """A dummy class for a singleton instance used to represent the constant. + + To represent linear expressions, we store a dictionary + MPVariable->coefficient. To represent the constant offset of the expression, + we use this class as a substitute: its coefficient will be the offset. To + properly be evaluated, its solution_value() needs to be 1. + """ + + def solution_value(self): # pylint: disable=invalid-name + return 1 + + def __repr__(self): + return 'OFFSET_KEY' + + +OFFSET_KEY = _FakeMPVariableRepresentingTheConstantOffset() + + +def CastToLinExp(v): + if isinstance(v, numbers.Number): + return Constant(v) + else: + return v + + class LinearExpr(object): """Holds linear expressions. @@ -34,109 +62,97 @@ class LinearExpr(object): floating-point value). """ - def IsConstant(self, expr): - return isinstance(expr, numbers.Number) + SUPPORTED_OPERATOR_METHODS = [ + '__%s__' % opname + for opname in ['add', 'radd', 'sub', 'rsub', 'mul', 'rmul', 'div', + 'truediv', 'neg', 'eq', 'ge', 'le'] + ] + # TODO(user): Remove Visit() in favor of GetCoeffs(). def Visit(self, coeffs): """Fills the coefficient dictionary, and returns the offset.""" - return self.DoVisit(coeffs, 1.0) - - def DoVisit(self, coeffs, multiplier): - """Like Visit, but do that with a global floating-point multiplier.""" - raise NotImplementedError + self.AddSelfToCoeffMap(coeffs, 1.0) + constant = coeffs.pop(OFFSET_KEY, 0.0) + return constant def solution_value(self): # pylint: disable=invalid-name """Value of this linear expr, using the solution_value of its vars.""" + coeffs = self.GetCoeffs() + return sum(var.solution_value() * coeff for var, coeff in coeffs.items()) + + def AddSelfToCoeffMap(self, coeffs, multiplier): + raise NotImplementedError + + def GetCoeffs(self): coeffs = {} - constant = self.Visit(coeffs) - return constant + sum( - var.solution_value() * coeff for var, coeff in sorted(coeffs.items())) + self.AddSelfToCoeffMap(coeffs, 1.0) + return coeffs def __add__(self, expr): - if self.IsConstant(expr): - return SumCst(self, expr) - else: - return Sum(self, expr) + return Sum(self, expr) def __radd__(self, cst): - if self.IsConstant(cst): - return SumCst(self, cst) - else: - raise TypeError + return Sum(self, cst) def __sub__(self, expr): - if self.IsConstant(expr): - return SumCst(self, -expr) - else: - return Sum(self, ProductCst(expr, -1)) + return Sum(self, -expr) def __rsub__(self, cst): - if self.IsConstant(cst): - return SumCst(ProductCst(self, -1), cst) - else: - raise TypeError + return Sum(-self, cst) def __mul__(self, cst): - if self.IsConstant(cst): - return ProductCst(self, cst) - else: - raise TypeError + return ProductCst(self, cst) def __rmul__(self, cst): - if self.IsConstant(cst): - return ProductCst(self, cst) - else: - raise TypeError + return ProductCst(self, cst) def __div__(self, cst): - if self.IsConstant(cst): - if cst == 0.0: - raise ZeroDivisionError - else: - return ProductCst(self, 1.0 / cst) - else: - raise TypeError + return ProductCst(self, 1.0 / cst) def __truediv__(self, cst): - if self.IsConstant(cst): - if cst == 0.0: - raise ZeroDivisionError - else: - return ProductCst(self, 1.0 / cst) - else: - raise TypeError + return ProductCst(self, 1.0 / cst) def __neg__(self): return ProductCst(self, -1) def __eq__(self, arg): - if self.IsConstant(arg): + if isinstance(arg, numbers.Number): return LinearConstraint(self, arg, arg) else: - return LinearConstraint(Sum(self, ProductCst(arg, -1)), 0.0, 0.0) - - def __hash__(self): - return object.__hash__(self) + return LinearConstraint(self - arg, 0.0, 0.0) def __ge__(self, arg): - if self.IsConstant(arg): - return LinearConstraint(self, arg, 1e308) + if isinstance(arg, numbers.Number): + return LinearConstraint(self, arg, inf) else: - return LinearConstraint(Sum(self, ProductCst(arg, -1)), 0.0, 1e308) + return LinearConstraint(self - arg, 0.0, inf) def __le__(self, arg): - if self.IsConstant(arg): - return LinearConstraint(self, -1e308, arg) + if isinstance(arg, numbers.Number): + return LinearConstraint(self, -inf, arg) else: - return LinearConstraint(Sum(self, ProductCst(arg, -1)), -1e308, 0.0) + return LinearConstraint(self - arg, -inf, 0.0) + + +class VariableExpr(LinearExpr): + """Represents a LinearExpr containing only a single variable.""" + + def __init__(self, mpvar): + self.__var = mpvar + + def AddSelfToCoeffMap(self, coeffs, multiplier): + coeffs[self.__var] = coeffs.get(self.__var, 0.0) + multiplier class ProductCst(LinearExpr): """Represents the product of a LinearExpr by a constant.""" def __init__(self, expr, coef): - self.__expr = expr - self.__coef = coef + self.__expr = CastToLinExp(expr) + if isinstance(coef, numbers.Number): + self.__coef = coef + else: + raise TypeError def __str__(self): if self.__coef == -1: @@ -144,64 +160,42 @@ class ProductCst(LinearExpr): else: return '(' + str(self.__coef) + ' * ' + str(self.__expr) + ')' - def DoVisit(self, coeffs, multiplier): + def AddSelfToCoeffMap(self, coeffs, multiplier): current_multiplier = multiplier * self.__coef if current_multiplier: - return self.__expr.DoVisit(coeffs, current_multiplier) - return 0.0 + self.__expr.AddSelfToCoeffMap(coeffs, current_multiplier) -class Sum(LinearExpr): - """Represents the sum of two LinearExpr.""" +class Constant(LinearExpr): - def __init__(self, left, right): - self.__left = left - self.__right = right + def __init__(self, val): + self.__val = val def __str__(self): - return '(' + str(self.__left) + ' + ' + str(self.__right) + ')' + return str(self.__val) - def DoVisit(self, coeffs, multiplier): - constant = self.__left.DoVisit(coeffs, multiplier) - constant += self.__right.DoVisit(coeffs, multiplier) - return constant + def AddSelfToCoeffMap(self, coeffs, multiplier): + coeffs[OFFSET_KEY] = coeffs.get(OFFSET_KEY, 0.0) + self.__val * multiplier class SumArray(LinearExpr): - """Represents the sum of an array of objects (constants or LinearExpr).""" + """Represents the sum of a list of LinearExpr.""" def __init__(self, array): - if type(array) is types.GeneratorType: - self.__array = [x for x in array] - else: - self.__array = array + self.__array = map(CastToLinExp, array) def __str__(self): - return 'Sum(' + str(self.__array) + ')' + return '({})'.format(' + '.join(map(str, self.__array))) - def DoVisit(self, coeffs, multiplier): - constant = 0.0 - for t in self.__array: - if self.IsConstant(t): - constant += t * multiplier - else: - constant += t.DoVisit(coeffs, multiplier) - return constant + def AddSelfToCoeffMap(self, coeffs, multiplier): + for arg in self.__array: + arg.AddSelfToCoeffMap(coeffs, multiplier) -class SumCst(LinearExpr): - """Represents the sum of a LinearExpr and a constant.""" +def Sum(*args): + return SumArray(args) - def __init__(self, expr, cst): - self.__expr = expr - self.__cst = cst - - def __str__(self): - return '(' + str(self.__expr) + ' + ' + str(self.__cst) + ')' - - def DoVisit(self, coeffs, multiplier): - constant = self.__expr.DoVisit(coeffs, multiplier) - return constant + self.__cst * multiplier +SumCst = Sum # pylint: disable=invalid-name class LinearConstraint(object): @@ -213,28 +207,28 @@ class LinearConstraint(object): self.__ub = ub def __str__(self): - if self.__lb > -1e308 and self.__ub < 1e308: + if self.__lb > -inf and self.__ub < inf: if self.__lb == self.__ub: return str(self.__expr) + ' == ' + str(self.__lb) else: return (str(self.__lb) + ' <= ' + str(self.__expr) + ' <= ' + str(self.__ub)) - elif self.__lb > -1e308: + elif self.__lb > -inf: return str(self.__expr) + ' >= ' + str(self.__lb) - elif self.__ub < 1e308: + elif self.__ub < inf: return str(self.__expr) + ' <= ' + str(self.__ub) else: return 'Trivial inequality (always true)' def Extract(self, solver, name=''): """Performs the actual creation of the constraint object.""" - coeffs = {} - constant = self.__expr.Visit(coeffs) + coeffs = self.__expr.GetCoeffs() + constant = coeffs.pop(OFFSET_KEY, 0.0) lb = -solver.infinity() ub = solver.infinity() - if self.__lb > -1e308: + if self.__lb > -inf: lb = self.__lb - constant - if self.__ub < 1e308: + if self.__ub < inf: ub = self.__ub - constant constraint = solver.RowConstraint(lb, ub, name) diff --git a/src/util/csharp/proto.swig b/src/util/csharp/proto.swig index 7fe8b145d7..1cd239bfe3 100644 --- a/src/util/csharp/proto.swig +++ b/src/util/csharp/proto.swig @@ -46,57 +46,62 @@ // @param CSharpProtoType the corresponding fully qualified C# protocol message // type // @param param_name the parameter name -// @param deleteCppReturn indicates that the resulting object is a native -// (java, c#, python) object, and thus the C++ proto can be safely deleted -// after the conversion. %define PROTO_INPUT(CppProtoType, CSharpProtoType, param_name) -%typemap(ctype) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "int proto_size, char*" +%typemap(ctype) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "int proto_size, uint8*" %typemap(imtype) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "int proto_size, byte[]" %typemap(cstype) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "CSharpProtoType" -%typemap(csin) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "$csinput.GetByteArrayLength(), $csinput.ToByteArray()" -%typemap(in) PROTO_TYPE* INPUT (CppProtoType temp), PROTO_TYPE& INPUT (CppProtoType temp) { - int proto_size = 0; - std::unique_ptr proto_buffer($input); - bool parsed_ok = temp.ParseFromArray(proto_buffer.get(), proto_size); +%typemap(csin) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "$csinput.CalculateSize(), ProtoHelper.ProtoToByteArray($csinput)" +%typemap(in) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT { + $1 = new CppProtoType; + bool parsed_ok = $1->ParseFromArray($input, proto_size); if (!parsed_ok) { SWIG_CSharpSetPendingException( SWIG_CSharpSystemException, "Unable to parse CppProtoType protocol message."); } - $1 = &temp; } - +%typemap(freearg) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT { + delete $1; + } %apply PROTO_TYPE& INPUT { const CppProtoType& param_name } %apply PROTO_TYPE& INPUT { CppProtoType& param_name } %apply PROTO_TYPE* INPUT { const CppProtoType* param_name } %apply PROTO_TYPE* INPUT { CppProtoType* param_name } - %enddef // end PROTO_INPUT -%define PROTO2_RETURN(CppProtoType, CSharpProtoType, deleteCppReturn) -%typemap(ctype) CppProtoType* "char*" -%typemap(imtype) CppProtoType* "byte[]" -%typemap(cstype) CppProtoType* "CSharpProtoType" -%typemap(csout) CppProtoType* { - byte[] buf = $imcall; - if (buf == null || buf.Length == 0) { - return null; - } +%define PROTO2_RETURN(CppProtoType, CSharpProtoType) +%typemap(ctype) CppProtoType "uint8*" +%typemap(imtype) CppProtoType "System.IntPtr" +%typemap(cstype) CppProtoType "CSharpProtoType" +%typemap(csout) CppProtoType { + byte[] tmp = new byte[4]; + System.IntPtr data = $imcall; + System.Runtime.InteropServices.Marshal.Copy(data, tmp, 0, 4); + int size = Convert.ToInt32(tmp[0]) + + Convert.ToInt32(tmp[1]) * 0xFF + + Convert.ToInt32(tmp[2]) * 0xFFFF + + Convert.ToInt32(tmp[3]) * 0xFFFFFF; + byte[] buf = new byte[size + 4]; + System.Runtime.InteropServices.Marshal.Copy(data, buf, 0, size + 4); + // TODO(user): delete the C++ buffer. try { - return CSharpProtoType.ParseFrom(buf); + Google.Protobuf.CodedInputStream input = + new Google.Protobuf.CodedInputStream(buf, 4, size); + CSharpProtoType proto = new CSharpProtoType(); + proto.MergeFrom(input); + return proto; } catch (Google.Protobuf.InvalidProtocolBufferException e) { throw new SystemException( "Unable to parse CSharpProtoType protocol message."); } } -%typemap(out) CppProtoType* { - std::unique_ptr buf(new char[$1->ByteSize()]); - $1->SerializeWithCachedSizesToArray(reinterpret_cast(buf.get())); - $result = buf.get(); - if (deleteCppReturn) { - // To prevent a memory leak. - delete $1; - $1 = NULL; - } +%typemap(out) CppProtoType { + const int size = $1.ByteSize(); + $result = new uint8[size + 4]; + $1.SerializeWithCachedSizesToArray($result + 4); + $result[0] = size & 0xFF; + $result[1] = (size << 8) & 0xFF; + $result[2] = (size << 16) & 0xFF; + $result[3] = (size << 24) & 0xFF; } -%enddef // end PROTO2_RETURN_AND_DELETE +%enddef // end PROTO2_RETURN diff --git a/src/util/java/proto.swig b/src/util/java/proto.swig index 5b8da1bdb6..ecf37d9f54 100644 --- a/src/util/java/proto.swig +++ b/src/util/java/proto.swig @@ -74,11 +74,11 @@ %enddef // PROTO_INPUT -%define PROTO2_RETURN(CppProtoType, JavaProtoType, giveOwnership) -%typemap(jni) CppProtoType* "jbyteArray" -%typemap(jtype) CppProtoType* "byte[]" -%typemap(jstype) CppProtoType* "JavaProtoType" -%typemap(javaout) CppProtoType* { +%define PROTO2_RETURN(CppProtoType, JavaProtoType) +%typemap(jni) CppProtoType "jbyteArray" +%typemap(jtype) CppProtoType "byte[]" +%typemap(jstype) CppProtoType "JavaProtoType" +%typemap(javaout) CppProtoType { byte[] buf = $jnicall; if (buf == null || buf.length == 0) { return null; @@ -90,14 +90,9 @@ "Unable to parse JavaProtoType protocol message."); } } -%typemap(out) CppProtoType* { - std::unique_ptr buf(new char[$1->ByteSize()]); - $1->SerializeWithCachedSizesToArray(reinterpret_cast(buf.get())); - $result = JNIUtil::MakeJByteArray(jenv, buf.get(), $1->ByteSize()); - if (giveOwnership) { - // To prevent a memory leak. - delete $1; - $1 = NULL; - } +%typemap(out) CppProtoType { + std::unique_ptr buf(new char[$1.ByteSize()]); + $1.SerializeWithCachedSizesToArray(reinterpret_cast(buf.get())); + $result = JNIUtil::MakeJByteArray(jenv, buf.get(), $1.ByteSize()); } %enddef // PROTO2_RETURN diff --git a/src/util/java/vector.swig b/src/util/java/vector.swig index ce9a80df8c..cae172e892 100644 --- a/src/util/java/vector.swig +++ b/src/util/java/vector.swig @@ -89,9 +89,9 @@ } %} %typemap(out) std::vector %{ - $result = jenv->New ## JavaTypeName ## Array($1.size()); - jenv->Set ## JavaTypeName ## ArrayRegion( - $result, 0, $1.size(), reinterpret_cast(&($1)[0])); + const std::vector& vec = $1; + $result = jenv->New ## JavaTypeName ## Array(vec.size()); + jenv->Set ## JavaTypeName ## ArrayRegion($result, 0, vec.size(), vec.data()); %} %typemap(javaout) std::vector { return $jnicall; diff --git a/src/util/python/proto.swig b/src/util/python/proto.swig new file mode 100644 index 0000000000..7d106d2a4e --- /dev/null +++ b/src/util/python/proto.swig @@ -0,0 +1,103 @@ +// 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. + +// TODO(user): make this SWIG file comply with the SWIG style guide. +%include "base/base.swig" + +%import "base/integral_types.h" + +namespace operations_research { +%define PY_PROTO_TYPEMAP(PythonModule, PythonType, CppType) +%typecheck(SWIG_TYPECHECK_POINTER) const CppType&, CppType* { + bool ok = false; + PyObject* const module = PyImport_ImportModule("PythonModule"); + if (module != nullptr) { + PyObject* const dict = PyModule_GetDict(module); + if (dict != nullptr) { + PyObject* const clss = PyDict_GetItemString(dict, "PythonType"); + if (clss != nullptr) { + if (PyObject_IsInstance($input, clss)) { + ok = true; + } + Py_DECREF(clss); + } + Py_DECREF(dict); + } + Py_DECREF(module); + } + $1 = ok ? 1 : 0; +} + +%typemap(in) const CppType&, CppType* const, CppType* { + $1 = new CppType; + PyObject* const pyresult = PyObject_CallMethod( + $input, const_cast("SerializeToString"), nullptr); + if (pyresult != nullptr) { + char* buffer = nullptr; + Py_ssize_t length = 0; + int result = PyString_AsStringAndSize(pyresult, &buffer, &length); + if (buffer != nullptr) { + $1->ParseFromArray(buffer, length); + } + Py_DECREF(pyresult); + } +} + +%typemap(freearg) const CppType&, CppType* const, CppType* { + delete $1; +} + +%typemap(argout) CppType* const, CppType* { + std::string encoded_protobuf; + $1->SerializeToString(&encoded_protobuf); + PyObject* const python_encoded_protobuf = + PyString_FromStringAndSize(encoded_protobuf.c_str(), + encoded_protobuf.size()); + if (python_encoded_protobuf != nullptr) { + PyObject* const result = PyObject_CallMethod( + $input, const_cast("ParseFromString"), + const_cast("(O)"), python_encoded_protobuf); + Py_DECREF(python_encoded_protobuf); + if (result != nullptr) { Py_DECREF(result); } + } +} + +%typemap(out) CppType { + PyObject* const module = PyImport_ImportModule("PythonModule"); + if (module != nullptr) { + PyObject* const dict = PyModule_GetDict(module); + if (dict != nullptr) { + PyObject* const clss = PyDict_GetItemString(dict, "PythonType"); + if (clss != nullptr) { + std::string encoded_protobuf; + $1.SerializeToString(&encoded_protobuf); + PyObject* const python_encoded_protobuf = PyString_FromStringAndSize( + encoded_protobuf.c_str(), encoded_protobuf.size()); + PyObject* const result = PyObject_CallMethod( + clss, const_cast("FromString"), + const_cast("(O)"), + python_encoded_protobuf); + // Py_DECREF(python_encoded_protobuf); + $result = result; + Py_DECREF(clss); + } + Py_DECREF(dict); + } + Py_DECREF(module); + } +} +%typemap(newfree) CppType { + delete $1; +} +%enddef // PY_PROTO_TYPEMAP +} // namespace operations_research diff --git a/tools/or-tools.nuspec b/tools/or-tools.nuspec index 00f1e45a90..2f51b706b2 100644 --- a/tools/or-tools.nuspec +++ b/tools/or-tools.nuspec @@ -2,13 +2,13 @@ Google.OrTools - 2.1.VVVV + 2.2.VVVV Google https://github.com/google/or-tools/blob/master/LICENSE-2.0.txt https://developers.google.com/optimization .NET Assembly for the OR-Tools project SVN Release VVVV. - Copyright 2015 Google + Copyright 2016 Google OperationsResearch MathProgramming LinearProgramming C++ Python Java .NET diff --git a/tools/setup.py b/tools/setup.py index 705fa40c8c..2b936a012c 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -16,7 +16,7 @@ DELETEUNIX extra_link_args=['/MANIFEST'], setup( name='ortools', - version='2.VVVV', + version='3.VVVV', packages=[ 'ortools', 'ortools.algorithms', diff --git a/tools/setup_data.py b/tools/setup_data.py index f38b17730d..850a86df0a 100644 --- a/tools/setup_data.py +++ b/tools/setup_data.py @@ -11,7 +11,7 @@ def read(fname): setup( name='ortools_examples', - version='2.VVVV', + version='3.VVVV', install_requires = ['ortools'], license='Apache 2.0', author = 'Google Inc', diff --git a/tools/setup_py3.py b/tools/setup_py3.py index c7a9df4c73..353a06d2a2 100644 --- a/tools/setup_py3.py +++ b/tools/setup_py3.py @@ -16,7 +16,7 @@ DELETEUNIX extra_link_args=['/MANIFEST'], setup( name='py3-ortools', - version='2.VVVV', + version='3.VVVV', packages=[ 'ortools', 'ortools.algorithms',