dotnet: Remove reference to dotnet release command
- Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
This commit is contained in:
@@ -1,17 +1,44 @@
|
||||
if(NOT BUILD_CXX)
|
||||
return()
|
||||
if (NOT BUILD_CXX)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(ortools_examples)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_INSTALL_RPATH
|
||||
"@loader_path/../..;@loader_path/../lib;@loader_path")
|
||||
if (APPLE)
|
||||
set(CMAKE_INSTALL_RPATH
|
||||
"@loader_path/../..;@loader_path/../lib;@loader_path")
|
||||
else()
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/../../:$ORIGIN/../lib:$ORIGIN/")
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/../../:$ORIGIN/../lib:$ORIGIN/")
|
||||
endif()
|
||||
|
||||
set(_SRCS
|
||||
cvrptw_lib.cc
|
||||
fap_model_printer.cc
|
||||
fap_parser.cc
|
||||
fap_utilities.cc
|
||||
parse_dimacs_assignment.cc
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
add_library(${PROJECT_NAME} STATIC ${_SRCS})
|
||||
else()
|
||||
add_library(${PROJECT_NAME} SHARED ${_SRCS})
|
||||
endif()
|
||||
|
||||
get_filename_component(PARENT_SOURCE_DIR ${PROJECT_SOURCE_DIR} DIRECTORY)
|
||||
get_filename_component(PARENT_SOURCE_DIR ${PARENT_SOURCE_DIR} DIRECTORY)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${PARENT_SOURCE_DIR})
|
||||
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC ortools::ortools)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}Targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
foreach(EXECUTABLE
|
||||
costas_array_sat
|
||||
|
||||
@@ -25,21 +25,28 @@
|
||||
#include <vector>
|
||||
|
||||
#include "examples/cpp/cvrptw_lib.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::Assignment;
|
||||
using operations_research::DefaultRoutingSearchParameters;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingIndexManager;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::RoutingNodeIndex;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::Solver;
|
||||
|
||||
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
@@ -48,6 +55,9 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_bool(vrp_use_same_vehicle_costs, false,
|
||||
"Use same vehicle costs in the routing model");
|
||||
DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
@@ -61,13 +71,9 @@ int main(int argc, char** argv) {
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
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);
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
@@ -79,35 +85,48 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
const operations_research::RoutingDimension& time_dimension =
|
||||
routing.GetDimensionOrDie(kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding disjoint time windows.
|
||||
operations_research::Solver* solver = routing.solver();
|
||||
Solver* solver = routing.solver();
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
std::vector<int64> forbid_points(2 * FLAGS_vrp_windows, 0);
|
||||
for (int i = 0; i < forbid_points.size(); ++i) {
|
||||
forbid_points[i] = randomizer.Uniform(kHorizon);
|
||||
@@ -126,19 +145,19 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Adding penalty costs to allow skipping orders.
|
||||
const int64 kPenalty = 10000000;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
std::vector<RoutingModel::NodeIndex> orders(1, order);
|
||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < manager.num_nodes(); ++order) {
|
||||
std::vector<int64> orders(1, manager.NodeToIndex(order));
|
||||
routing.AddDisjunction(orders, kPenalty);
|
||||
}
|
||||
|
||||
// Adding same vehicle constraint costs for consecutive nodes.
|
||||
if (FLAGS_vrp_use_same_vehicle_costs) {
|
||||
std::vector<RoutingModel::NodeIndex> group;
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
group.push_back(order);
|
||||
std::vector<int64> group;
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < manager.num_nodes(); ++order) {
|
||||
group.push_back(manager.NodeToIndex(order));
|
||||
if (group.size() == kMaxNodesPerGroup) {
|
||||
routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost);
|
||||
group.clear();
|
||||
@@ -150,10 +169,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(routing, *solution, FLAGS_vrp_use_same_vehicle_costs,
|
||||
DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs,
|
||||
kMaxNodesPerGroup, kSameVehicleCost,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
|
||||
@@ -24,20 +24,26 @@
|
||||
#include <vector>
|
||||
|
||||
#include "examples/cpp/cvrptw_lib.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::Assignment;
|
||||
using operations_research::DefaultRoutingSearchParameters;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingIndexManager;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::RoutingNodeIndex;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
|
||||
@@ -47,6 +53,9 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_bool(vrp_use_same_vehicle_costs, false,
|
||||
"Use same vehicle costs in the routing model");
|
||||
DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
@@ -60,13 +69,9 @@ int main(int argc, char** argv) {
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
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);
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
@@ -78,54 +83,67 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
||||
const operations_research::RoutingDimension& time_dimension =
|
||||
routing.GetDimensionOrDie(kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
||||
}
|
||||
|
||||
// Adding penalty costs to allow skipping orders.
|
||||
const int64 kPenalty = 10000000;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
std::vector<RoutingModel::NodeIndex> orders(1, order);
|
||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < manager.num_nodes(); ++order) {
|
||||
std::vector<int64> orders(1, manager.NodeToIndex(order));
|
||||
routing.AddDisjunction(orders, kPenalty);
|
||||
}
|
||||
|
||||
// Adding same vehicle constraint costs for consecutive nodes.
|
||||
if (FLAGS_vrp_use_same_vehicle_costs) {
|
||||
std::vector<RoutingModel::NodeIndex> group;
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
group.push_back(order);
|
||||
std::vector<int64> group;
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < manager.num_nodes(); ++order) {
|
||||
group.push_back(manager.NodeToIndex(order));
|
||||
if (group.size() == kMaxNodesPerGroup) {
|
||||
routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost);
|
||||
group.clear();
|
||||
@@ -137,10 +155,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(routing, *solution, FLAGS_vrp_use_same_vehicle_costs,
|
||||
DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs,
|
||||
kMaxNodesPerGroup, kSameVehicleCost,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
|
||||
@@ -37,17 +37,15 @@ class LocationContainer {
|
||||
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;
|
||||
int64 ManhattanDistance(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
int64 NegManhattanDistance(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
int64 ManhattanTime(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
|
||||
bool SameLocation(operations_research::RoutingModel::NodeIndex node1,
|
||||
operations_research::RoutingModel::NodeIndex node2) const;
|
||||
bool SameLocation(RoutingIndexManager::NodeIndex node1,
|
||||
RoutingIndexManager::NodeIndex node2) const;
|
||||
int64 SameLocationFromIndex(int64 node1, int64 node2) const;
|
||||
|
||||
private:
|
||||
@@ -67,23 +65,22 @@ class LocationContainer {
|
||||
|
||||
MTRandom randomizer_;
|
||||
const int64 speed_;
|
||||
gtl::ITIVector<operations_research::RoutingModel::NodeIndex, Location>
|
||||
locations_;
|
||||
gtl::ITIVector<RoutingIndexManager::NodeIndex, Location> locations_;
|
||||
};
|
||||
|
||||
// Random demand.
|
||||
class RandomDemand {
|
||||
public:
|
||||
RandomDemand(int size, operations_research::RoutingModel::NodeIndex depot,
|
||||
RandomDemand(int size, RoutingIndexManager::NodeIndex depot,
|
||||
bool use_deterministic_seed);
|
||||
void Initialize();
|
||||
int64 Demand(operations_research::RoutingModel::NodeIndex from,
|
||||
operations_research::RoutingModel::NodeIndex to) const;
|
||||
int64 Demand(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<int64[]> demand_;
|
||||
const int size_;
|
||||
const operations_research::RoutingModel::NodeIndex depot_;
|
||||
const RoutingIndexManager::NodeIndex depot_;
|
||||
const bool use_deterministic_seed_;
|
||||
};
|
||||
|
||||
@@ -92,16 +89,15 @@ 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;
|
||||
operations_research::RoutingNodeEvaluator2 demand,
|
||||
operations_research::RoutingNodeEvaluator2 transition_time);
|
||||
int64 Compute(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
const int64 time_per_demand_unit_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2>
|
||||
transition_time_;
|
||||
operations_research::RoutingNodeEvaluator2 demand_;
|
||||
operations_research::RoutingNodeEvaluator2 transition_time_;
|
||||
};
|
||||
|
||||
// Stop service time + transition time callback.
|
||||
@@ -109,21 +105,21 @@ 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;
|
||||
operations_research::RoutingNodeEvaluator2 transition_time);
|
||||
int64 Compute(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
const int64 stop_time_;
|
||||
const LocationContainer& location_container_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2>
|
||||
transition_time_;
|
||||
operations_research::RoutingNodeEvaluator2 demand_;
|
||||
operations_research::RoutingNodeEvaluator2 transition_time_;
|
||||
};
|
||||
|
||||
// Route plan displayer.
|
||||
// TODO(user): Move the display code to the routing library.
|
||||
void DisplayPlan(
|
||||
const operations_research::RoutingIndexManager& manager,
|
||||
const operations_research::RoutingModel& routing,
|
||||
const operations_research::Assignment& plan, bool use_same_vehicle_costs,
|
||||
int64 max_nodes_per_group, int64 same_vehicle_cost,
|
||||
|
||||
@@ -27,29 +27,42 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "examples/cpp/cvrptw_lib.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/join.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::Assignment;
|
||||
using operations_research::DefaultRoutingSearchParameters;
|
||||
using operations_research::FirstSolutionStrategy;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::IntervalVar;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingIndexManager;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::RoutingNodeIndex;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::Solver;
|
||||
|
||||
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
@@ -61,14 +74,14 @@ int main(int argc, char** argv) {
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingSearchParameters parameters =
|
||||
operations_research::BuildSearchParametersFromFlags();
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingModel routing(manager);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
parameters.set_first_solution_strategy(
|
||||
operations_research::FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION);
|
||||
parameters.mutable_local_search_operators()->set_use_path_lns(false);
|
||||
parameters.mutable_local_search_operators()->set_use_inactive_lns(false);
|
||||
FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
@@ -80,35 +93,48 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
operations_research::RoutingDimension* const time_dimension =
|
||||
routing.GetMutableDimension(kTime);
|
||||
RoutingDimension* const time_dimension = routing.GetMutableDimension(kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
time_dimension->CumulVar(order)->SetRange(start, start + kTWDuration);
|
||||
routing.AddToAssignment(time_dimension->SlackVar(order));
|
||||
@@ -129,19 +155,24 @@ int main(int argc, char** argv) {
|
||||
// - 40min breaks between 11:00am and 1:00pm
|
||||
// or
|
||||
// - 2 x 30min breaks between 10:00am and 3:00pm, at least 1h apart
|
||||
// First, fill service time vector.
|
||||
std::vector<int64> service_times(routing.Size());
|
||||
for (int node = 0; node < routing.Size(); node++) {
|
||||
const RoutingIndexManager::NodeIndex index(node);
|
||||
service_times[node] = kTimePerDemandUnit * demand.Demand(index, index);
|
||||
if (node >= routing.nodes()) service_times[node] = 0;
|
||||
}
|
||||
const std::vector<std::vector<int>> break_data = {
|
||||
{/*start_min*/ 11, /*start_max*/ 13, /*duration*/ 2400},
|
||||
{/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800},
|
||||
{/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800}};
|
||||
operations_research::Solver* const solver = routing.solver();
|
||||
Solver* const solver = routing.solver();
|
||||
for (int vehicle = 0; vehicle < FLAGS_vrp_vehicles; ++vehicle) {
|
||||
std::vector<operations_research::IntervalVar*> breaks;
|
||||
std::vector<IntervalVar*> breaks;
|
||||
for (int i = 0; i < break_data.size(); ++i) {
|
||||
operations_research::IntervalVar* const break_interval =
|
||||
solver->MakeFixedDurationIntervalVar(
|
||||
break_data[i][0] * 3600, break_data[i][1] * 3600,
|
||||
break_data[i][2], true,
|
||||
absl::StrCat("Break ", i, " on vehicle ", vehicle));
|
||||
IntervalVar* const break_interval = solver->MakeFixedDurationIntervalVar(
|
||||
break_data[i][0] * 3600, break_data[i][1] * 3600, break_data[i][2],
|
||||
true, absl::StrCat("Break ", i, " on vehicle ", vehicle));
|
||||
breaks.push_back(break_interval);
|
||||
}
|
||||
// break1 performed iff break2 performed
|
||||
@@ -149,27 +180,26 @@ int main(int argc, char** argv) {
|
||||
breaks[2]->PerformedExpr()));
|
||||
// break2 start 1h after break1.
|
||||
solver->AddConstraint(solver->MakeIntervalVarRelationWithDelay(
|
||||
breaks[2], operations_research::Solver::STARTS_AFTER_END, breaks[1],
|
||||
3600));
|
||||
breaks[2], Solver::STARTS_AFTER_END, breaks[1], 3600));
|
||||
// break0 performed iff break2 unperformed
|
||||
solver->AddConstraint(solver->MakeNonEquality(breaks[0]->PerformedExpr(),
|
||||
breaks[2]->PerformedExpr()));
|
||||
|
||||
time_dimension->SetBreakIntervalsOfVehicle(std::move(breaks), vehicle);
|
||||
time_dimension->SetBreakIntervalsOfVehicle(std::move(breaks), vehicle,
|
||||
service_times);
|
||||
}
|
||||
|
||||
// Adding penalty costs to allow skipping orders.
|
||||
const int64 kPenalty = 10000000;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
std::vector<RoutingModel::NodeIndex> orders(1, order);
|
||||
std::vector<int64> orders(1, manager.NodeToIndex(order));
|
||||
routing.AddDisjunction(orders, kPenalty);
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
LOG(INFO) << "Breaks: ";
|
||||
for (const auto& break_interval :
|
||||
@@ -181,7 +211,7 @@ int main(int argc, char** argv) {
|
||||
LOG(INFO) << break_interval.Var()->name() << " unperformed";
|
||||
}
|
||||
}
|
||||
DisplayPlan(routing, *solution, false, 0, 0,
|
||||
DisplayPlan(manager, routing, *solution, false, 0, 0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
} else {
|
||||
|
||||
@@ -22,29 +22,36 @@
|
||||
#include <vector>
|
||||
|
||||
#include "examples/cpp/cvrptw_lib.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::Assignment;
|
||||
using operations_research::DefaultRoutingSearchParameters;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingIndexManager;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::RoutingNodeIndex;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::StringAppendF;
|
||||
using operations_research::StringPrintf;
|
||||
|
||||
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
@@ -63,13 +70,9 @@ int main(int argc, char** argv) {
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
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);
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
@@ -81,34 +84,47 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
||||
const operations_research::RoutingDimension& time_dimension =
|
||||
routing.GetDimensionOrDie(kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
if (!IsRefuelNode(order)) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
||||
@@ -120,11 +136,12 @@ int main(int argc, char** argv) {
|
||||
// increase by letting slack variable replenish the fuel.
|
||||
const int64 kFuelCapacity = kXMax + kYMax;
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&locations,
|
||||
&LocationContainer::NegManhattanDistance),
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.NegManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
}),
|
||||
kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, kFuel);
|
||||
const operations_research::RoutingDimension& fuel_dimension =
|
||||
routing.GetDimensionOrDie(kFuel);
|
||||
const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel);
|
||||
for (int order = 0; order < routing.Size(); ++order) {
|
||||
// Only let slack free for refueling nodes.
|
||||
if (!IsRefuelNode(order) || routing.IsStart(order)) {
|
||||
@@ -136,18 +153,20 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Adding penalty costs to allow skipping orders.
|
||||
const int64 kPenalty = 100000;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
std::vector<RoutingModel::NodeIndex> orders(1, order);
|
||||
std::vector<int64> orders(1, manager.NodeToIndex(order));
|
||||
routing.AddDisjunction(orders, kPenalty);
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
|
||||
@@ -24,29 +24,39 @@
|
||||
#include <vector>
|
||||
|
||||
#include "examples/cpp/cvrptw_lib.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::Assignment;
|
||||
using operations_research::DefaultRoutingSearchParameters;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::IntervalVar;
|
||||
using operations_research::IntVar;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingIndexManager;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::RoutingNodeIndex;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::StringAppendF;
|
||||
using operations_research::StringPrintf;
|
||||
using operations_research::Solver;
|
||||
|
||||
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
@@ -58,13 +68,9 @@ int main(int argc, char** argv) {
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
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);
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
@@ -76,48 +82,63 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
routing.CumulVar(order, kTime)->SetRange(start, start + kTWDuration);
|
||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
||||
}
|
||||
|
||||
// Adding resource constraints at the depot (start and end location of
|
||||
// routes).
|
||||
std::vector<operations_research::IntVar*> start_end_times;
|
||||
std::vector<IntVar*> 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));
|
||||
start_end_times.push_back(time_dimension.CumulVar(routing.End(i)));
|
||||
start_end_times.push_back(time_dimension.CumulVar(routing.Start(i)));
|
||||
}
|
||||
// Build corresponding time intervals.
|
||||
const int64 kVehicleSetup = 180;
|
||||
operations_research::Solver* const solver = routing.solver();
|
||||
std::vector<operations_research::IntervalVar*> intervals;
|
||||
Solver* const solver = routing.solver();
|
||||
std::vector<IntervalVar*> intervals;
|
||||
solver->MakeFixedDurationIntervalVarArray(start_end_times, kVehicleSetup,
|
||||
"depot_interval", &intervals);
|
||||
// Constrain the number of maximum simultaneous intervals at depot.
|
||||
@@ -132,18 +153,20 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Adding penalty costs to allow skipping orders.
|
||||
const int64 kPenalty = 100000;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
std::vector<RoutingModel::NodeIndex> orders(1, order);
|
||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < manager.num_nodes(); ++order) {
|
||||
std::vector<int64> orders(1, manager.NodeToIndex(order));
|
||||
routing.AddDisjunction(orders, kPenalty);
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
|
||||
@@ -21,33 +21,42 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "examples/cpp/cvrptw_lib.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/join.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::Assignment;
|
||||
using operations_research::DefaultRoutingSearchParameters;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::IntervalVar;
|
||||
using operations_research::IntVar;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingIndexManager;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::RoutingNodeIndex;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::Solver;
|
||||
using operations_research::StopServiceTimePlusTransition;
|
||||
using operations_research::StringAppendF;
|
||||
using operations_research::StringPrintf;
|
||||
|
||||
DEFINE_int32(vrp_stops, 25, "Stop locations in the problem.");
|
||||
DEFINE_int32(vrp_orders_per_stop, 5, "Nodes for each stop.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
@@ -61,13 +70,9 @@ int main(int argc, char** argv) {
|
||||
const int vrp_orders = FLAGS_vrp_stops * FLAGS_vrp_orders_per_stop;
|
||||
// Nodes are indexed from 0 to vrp_orders, the starts and ends of the routes
|
||||
// are at node 0.
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
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);
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
@@ -80,28 +85,40 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kStopTime = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
StopServiceTimePlusTransition time(
|
||||
kStopTime, locations,
|
||||
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &StopServiceTimePlusTransition::Compute),
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding time windows, for the sake of simplicty same for each stop.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
@@ -111,12 +128,12 @@ int main(int argc, char** argv) {
|
||||
for (int stop_order = 0; stop_order < FLAGS_vrp_orders_per_stop;
|
||||
++stop_order) {
|
||||
const int order = stop * FLAGS_vrp_orders_per_stop + stop_order + 1;
|
||||
routing.CumulVar(order, kTime)->SetRange(start, start + kTWDuration);
|
||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Adding resource constraints at order locations.
|
||||
operations_research::Solver* const solver = routing.solver();
|
||||
Solver* const solver = routing.solver();
|
||||
std::vector<IntervalVar*> intervals;
|
||||
for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) {
|
||||
std::vector<IntervalVar*> stop_intervals;
|
||||
@@ -128,15 +145,14 @@ int main(int argc, char** argv) {
|
||||
intervals.push_back(interval);
|
||||
stop_intervals.push_back(interval);
|
||||
// Link order and interval.
|
||||
operations_research::IntVar* const order_start =
|
||||
routing.CumulVar(order, kTime);
|
||||
IntVar* const order_start = time_dimension.CumulVar(order);
|
||||
solver->AddConstraint(
|
||||
solver->MakeIsEqualCt(interval->SafeStartExpr(0), order_start,
|
||||
interval->PerformedExpr()->Var()));
|
||||
// Make interval performed iff corresponding order has service time.
|
||||
// An order has no service time iff it is at the same location as the
|
||||
// next order on the route.
|
||||
operations_research::IntVar* const is_null_duration =
|
||||
IntVar* const is_null_duration =
|
||||
solver
|
||||
->MakeElement(
|
||||
[&locations, order](int64 index) {
|
||||
@@ -157,25 +173,27 @@ int main(int argc, char** argv) {
|
||||
stop_intervals, location_usage, 1, absl::StrCat("Client", stop)));
|
||||
}
|
||||
// Minimizing route duration.
|
||||
for (int vehicle = 0; vehicle < routing.vehicles(); ++vehicle) {
|
||||
for (int vehicle = 0; vehicle < manager.num_vehicles(); ++vehicle) {
|
||||
routing.AddVariableMinimizedByFinalizer(
|
||||
routing.CumulVar(routing.End(vehicle), kTime));
|
||||
time_dimension.CumulVar(routing.End(vehicle)));
|
||||
}
|
||||
|
||||
// Adding penalty costs to allow skipping orders.
|
||||
const int64 kPenalty = 100000;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
std::vector<RoutingModel::NodeIndex> orders(1, order);
|
||||
std::vector<int64> orders(1, manager.NodeToIndex(order));
|
||||
routing.AddDisjunction(orders, kPenalty);
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "examples/cpp/parse_dimacs_assignment.h"
|
||||
#include "examples/cpp/print_dimacs_assignment.h"
|
||||
#include "ortools/algorithms/hungarian.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/graph/ebert_graph.h"
|
||||
#include "ortools/graph/linear_assignment.h"
|
||||
@@ -89,8 +89,8 @@ CostValue BuildAndSolveHungarianInstance(
|
||||
hungarian_cost[tail][head] = cost;
|
||||
}
|
||||
}
|
||||
std::unordered_map<int, int> result;
|
||||
std::unordered_map<int, int> wish_this_could_be_null;
|
||||
absl::flat_hash_map<int, int> result;
|
||||
absl::flat_hash_map<int, int> wish_this_could_be_null;
|
||||
WallTimer timer;
|
||||
VLOG(1) << "Beginning Hungarian method.";
|
||||
timer.Start();
|
||||
@@ -177,14 +177,13 @@ using ::operations_research::ForwardStarGraph;
|
||||
using ::operations_research::ForwardStarStaticGraph;
|
||||
using ::operations_research::SolveDimacsAssignment;
|
||||
using ::operations_research::StarGraph;
|
||||
using ::operations_research::StringPrintf;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string usage;
|
||||
if (argc < 1) {
|
||||
usage = StringPrintf(kUsageTemplate, "solve_dimacs_assignment");
|
||||
usage = absl::StrFormat(kUsageTemplate, "solve_dimacs_assignment");
|
||||
} else {
|
||||
usage = StringPrintf(kUsageTemplate, argv[0]);
|
||||
usage = absl::StrFormat(kUsageTemplate, argv[0]);
|
||||
}
|
||||
gflags::SetUsageMessage(usage);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// generalized: we have N cards, each with K different symbols, and
|
||||
// there are N different symbols overall.
|
||||
//
|
||||
// This is a feasability problem. We transform that into an
|
||||
// This is a feasibility problem. We transform that into an
|
||||
// optimization problem where we penalize cards whose intersection is
|
||||
// of cardinality different from 1. A feasible solution of the
|
||||
// original problem is a solution with a zero cost.
|
||||
@@ -32,11 +32,11 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/constraint_solver/constraint_solveri.h"
|
||||
#include "ortools/util/bitset.h"
|
||||
|
||||
@@ -640,7 +640,8 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
|
||||
std::vector<std::vector<IntVar*> > card_symbol_vars(num_cards);
|
||||
std::vector<IntVar*> all_card_symbol_vars;
|
||||
for (int card_index = 0; card_index < num_cards; ++card_index) {
|
||||
solver.MakeBoolVarArray(num_symbols, StringPrintf("card_%i_", card_index),
|
||||
solver.MakeBoolVarArray(num_symbols,
|
||||
absl::StrFormat("card_%i_", card_index),
|
||||
&card_symbol_vars[card_index]);
|
||||
for (int symbol_index = 0; symbol_index < num_symbols; ++symbol_index) {
|
||||
all_card_symbol_vars.push_back(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2018 Google LLC
|
||||
// Copyright 2010-2017 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -21,12 +21,11 @@
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "ortools/base/file.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/split.h"
|
||||
#include "ortools/base/strtoint.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -236,7 +235,7 @@ class ParametersParser {
|
||||
void FindComponents(const std::vector<FapConstraint>& constraints,
|
||||
const std::map<int, FapVariable>& variables,
|
||||
const int maximum_variable_id,
|
||||
std::unordered_map<int, FapComponent>* components);
|
||||
absl::flat_hash_map<int, FapComponent>* components);
|
||||
|
||||
// Function that computes the impact of a constraint.
|
||||
int EvaluateConstraintImpact(const std::map<int, FapVariable>& variables,
|
||||
@@ -248,7 +247,7 @@ void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
std::map<int, FapVariable>* variables,
|
||||
std::vector<FapConstraint>* constraints,
|
||||
std::string* objective, std::vector<int>* frequencies,
|
||||
std::unordered_map<int, FapComponent>* components);
|
||||
absl::flat_hash_map<int, FapComponent>* components);
|
||||
|
||||
void ParseFileByLines(const std::string& filename,
|
||||
std::vector<std::string>* lines) {
|
||||
@@ -603,5 +602,6 @@ void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_
|
||||
|
||||
@@ -330,7 +330,7 @@ bool ConstraintImpactComparator(FapConstraint constraint1,
|
||||
}
|
||||
|
||||
int64 ValueEvaluator(
|
||||
std::unordered_map<int64, std::pair<int64, int64>>* value_evaluator_map,
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64>>* value_evaluator_map,
|
||||
int64 variable_index, int64 value) {
|
||||
CHECK(value_evaluator_map != nullptr);
|
||||
// Evaluate the choice. Smaller ranking denotes a better choice.
|
||||
@@ -343,7 +343,7 @@ int64 ValueEvaluator(
|
||||
}
|
||||
|
||||
// Update the history of assigned values and their rankings of each variable.
|
||||
std::unordered_map<int64, std::pair<int64, int64>>::iterator it;
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64>>::iterator it;
|
||||
int64 new_value = value;
|
||||
int64 new_ranking = ranking;
|
||||
if ((it = value_evaluator_map->find(variable_index)) !=
|
||||
@@ -578,7 +578,7 @@ void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
ChooseVariableStrategy(&variable_strategy);
|
||||
// Choose the value selection strategy.
|
||||
DecisionBuilder* db;
|
||||
std::unordered_map<int64, std::pair<int64, int64>> history;
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64>> history;
|
||||
if (FLAGS_value_evaluator == "value_evaluator") {
|
||||
LOG(INFO) << "Using ValueEvaluator for value selection strategy.";
|
||||
Solver::IndexEvaluator2 index_evaluator2 = [&history](int64 var,
|
||||
@@ -863,7 +863,7 @@ int main(int argc, char** argv) {
|
||||
std::vector<operations_research::FapConstraint> constraints;
|
||||
std::string objective;
|
||||
std::vector<int> values;
|
||||
std::unordered_map<int, operations_research::FapComponent> components;
|
||||
absl::flat_hash_map<int, operations_research::FapComponent> components;
|
||||
operations_research::ParseInstance(FLAGS_directory, FLAGS_find_components,
|
||||
&variables, &constraints, &objective,
|
||||
&values, &components);
|
||||
|
||||
@@ -16,13 +16,11 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "google/protobuf/wrappers.pb.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/join.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/stringpiece_utils.h"
|
||||
#include "ortools/base/strutil.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/data/jobshop_scheduling.pb.h"
|
||||
#include "ortools/data/jobshop_scheduling_parser.h"
|
||||
@@ -87,8 +85,8 @@ int64 ComputeHorizon(const JsspInputProblem& problem) {
|
||||
sum_of_transitions += max_transition;
|
||||
}
|
||||
}
|
||||
return std::min(max_latest_end,
|
||||
sum_of_durations + sum_of_transitions + max_earliest_start);
|
||||
return std::min<int64>(max_latest_end, sum_of_durations + sum_of_transitions +
|
||||
max_earliest_start);
|
||||
// TODO(user): Uses transitions.
|
||||
}
|
||||
|
||||
@@ -376,7 +374,7 @@ void Solve(const JsspInputProblem& problem) {
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
base::SetFlag(&FLAGS_logtostderr, true);
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_input.empty()) {
|
||||
LOG(FATAL) << "Please supply a data file with --input=";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2018 Google LLC
|
||||
// Copyright 2010-2017 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "google/protobuf/descriptor.h"
|
||||
#include "google/protobuf/message.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
@@ -24,14 +25,13 @@
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/status.h"
|
||||
#include "ortools/base/stringpiece_utils.h"
|
||||
#include "ortools/base/strutil.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/glop/lp_solver.h"
|
||||
#include "ortools/glop/parameters.pb.h"
|
||||
#include "ortools/lp_data/lp_print_utils.h"
|
||||
#include "ortools/lp_data/mps_reader.h"
|
||||
#include "ortools/lp_data/proto_utils.h"
|
||||
#include "ortools/util/file_util.h"
|
||||
#include "ortools/util/proto_tools.h"
|
||||
|
||||
DEFINE_bool(mps_dump_problem, false, "Dumps problem in readable form.");
|
||||
@@ -50,6 +50,7 @@ DEFINE_string(params, "",
|
||||
|
||||
using google::protobuf::TextFormat;
|
||||
using operations_research::FullProtocolMessageAsString;
|
||||
using operations_research::ReadFileToProto;
|
||||
using operations_research::glop::GetProblemStatusString;
|
||||
using operations_research::glop::GlopParameters;
|
||||
using operations_research::glop::LinearProgram;
|
||||
@@ -63,7 +64,9 @@ using operations_research::glop::ToDouble;
|
||||
void ReadGlopParameters(GlopParameters* parameters) {
|
||||
if (!FLAGS_params_file.empty()) {
|
||||
std::string params;
|
||||
CHECK(TextFormat::ParseFromString(params, parameters)) << params;
|
||||
CHECK_OK(file::GetContents(FLAGS_params_file, ¶ms, file::Defaults()));
|
||||
CHECK(TextFormat::MergeFromString(params, parameters))
|
||||
<< FLAGS_params;
|
||||
}
|
||||
if (!FLAGS_params.empty()) {
|
||||
CHECK(TextFormat::MergeFromString(FLAGS_params, parameters))
|
||||
@@ -89,15 +92,15 @@ int main(int argc, char* argv[]) {
|
||||
const std::string& file_name = file_list[i];
|
||||
MPSReader mps_reader;
|
||||
operations_research::MPModelProto model_proto;
|
||||
if (strings::EndsWith(file_name, ".mps") ||
|
||||
strings::EndsWith(file_name, ".mps.gz")) {
|
||||
if (absl::EndsWith(file_name, ".mps") ||
|
||||
absl::EndsWith(file_name, ".mps.gz")) {
|
||||
if (!mps_reader.LoadFileAndTryFreeFormOnFail(file_name,
|
||||
&linear_program)) {
|
||||
LOG(INFO) << "Parse error for " << file_name;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
file::ReadFileToProto(file_name, &model_proto);
|
||||
ReadFileToProto(file_name, &model_proto);
|
||||
MPModelProtoToLinearProgram(model_proto, &linear_program);
|
||||
}
|
||||
if (FLAGS_mps_dump_problem) {
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/constraint_solver/constraint_solveri.h"
|
||||
|
||||
DEFINE_bool(print, false, "If true, print one of the solution.");
|
||||
@@ -197,7 +197,8 @@ void NQueens(int size) {
|
||||
// model
|
||||
std::vector<IntVar*> queens;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
queens.push_back(s.MakeIntVar(0, size - 1, StringPrintf("queen%04d", i)));
|
||||
queens.push_back(
|
||||
s.MakeIntVar(0, size - 1, absl::StrFormat("queen%04d", i)));
|
||||
}
|
||||
s.AddConstraint(s.MakeAllDifferent(queens));
|
||||
|
||||
@@ -260,7 +261,7 @@ void NQueens(int size) {
|
||||
}
|
||||
}
|
||||
printf("========= number of solutions:%d\n", num_solutions);
|
||||
printf(" number of failures: %lld\n", s.failures());
|
||||
absl::PrintF(" number of failures: %d\n", s.failures());
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "ortools/base/filelineiter.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/macros.h"
|
||||
#include "ortools/base/split.h"
|
||||
#include "ortools/base/strtoint.h"
|
||||
#include "ortools/sat/boolean_problem.pb.h"
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
// - each node must be visited within its time window (time range during which
|
||||
// the node is accessible).
|
||||
// The maximum number of vehicles used (i.e. the number of routes used) is
|
||||
// specified in the data but can be overriden using the --pdp_force_vehicles
|
||||
// specified in the data but can be overridden using the --pdp_force_vehicles
|
||||
// flag.
|
||||
//
|
||||
// A further description of the problem can be found here:
|
||||
@@ -36,24 +36,35 @@
|
||||
// Reads data in the format defined by Li & Lim
|
||||
// (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/).
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/mathutil.h"
|
||||
#include "ortools/base/split.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/base/strtoint.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
DEFINE_string(pdp_file, "",
|
||||
"File containing the Pickup and Delivery Problem to solve.");
|
||||
DEFINE_int32(pdp_force_vehicles, 0,
|
||||
"Force the number of vehicles used (maximum number of routes.");
|
||||
DECLARE_string(routing_first_solution);
|
||||
DEFINE_bool(reduce_vehicle_cost_model, true,
|
||||
"Overrides the homonymous field of "
|
||||
"DefaultRoutingModelParameters().");
|
||||
DEFINE_string(routing_search_parameters,
|
||||
"first_solution_strategy:ALL_UNPERFORMED",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -67,8 +78,9 @@ typedef std::vector<std::pair<int, int> > Coordinates;
|
||||
|
||||
// Returns the scaled Euclidean distance between two nodes, coords holding the
|
||||
// coordinates of the nodes.
|
||||
int64 Travel(const Coordinates* const coords, RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) {
|
||||
int64 Travel(const Coordinates* const coords,
|
||||
RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) {
|
||||
DCHECK(coords != nullptr);
|
||||
const int xd = coords->at(from.value()).first - coords->at(to.value()).first;
|
||||
const int yd =
|
||||
@@ -79,30 +91,53 @@ int64 Travel(const Coordinates* const coords, RoutingModel::NodeIndex from,
|
||||
// Returns the scaled service time at a given node, service_times holding the
|
||||
// service times.
|
||||
int64 ServiceTime(const std::vector<int64>* const service_times,
|
||||
RoutingModel::NodeIndex node) {
|
||||
RoutingIndexManager::NodeIndex node) {
|
||||
return kScalingFactor * service_times->at(node.value());
|
||||
}
|
||||
|
||||
// Returns the scaled (distance plus service time) between two nodes, coords
|
||||
// Returns the scaled (distance plus service time) between two indices, coords
|
||||
// holding the coordinates of the nodes and service_times holding the service
|
||||
// times.
|
||||
// The service time is the time spent to execute a delivery or a pickup.
|
||||
int64 TravelPlusServiceTime(const Coordinates* const coords,
|
||||
int64 TravelPlusServiceTime(const RoutingIndexManager& manager,
|
||||
const Coordinates* const coords,
|
||||
const std::vector<int64>* const service_times,
|
||||
RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) {
|
||||
int64 from_index, int64 to_index) {
|
||||
const RoutingIndexManager::NodeIndex from = manager.IndexToNode(from_index);
|
||||
const RoutingIndexManager::NodeIndex to = manager.IndexToNode(to_index);
|
||||
return ServiceTime(service_times, from) + Travel(coords, from, to);
|
||||
}
|
||||
|
||||
// Returns the demand (quantity picked up or delivered) of a node, demands
|
||||
// holds the demand of each node.
|
||||
int64 Demand(const std::vector<int64>* const demands,
|
||||
RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) {
|
||||
return demands->at(from.value());
|
||||
// Returns the list of variables to use for the Tabu metaheuristic.
|
||||
// The current list is:
|
||||
// - Total cost of the solution,
|
||||
// - Number of used vehicles,
|
||||
// - Total schedule duration.
|
||||
// TODO(user): add total waiting time.
|
||||
std::vector<IntVar*> GetTabuVars(std::vector<IntVar*> existing_vars,
|
||||
operations_research::RoutingModel* routing) {
|
||||
Solver* const solver = routing->solver();
|
||||
std::vector<IntVar*> vars(std::move(existing_vars));
|
||||
vars.push_back(routing->CostVar());
|
||||
|
||||
IntVar* used_vehicles = solver->MakeIntVar(0, routing->vehicles());
|
||||
std::vector<IntVar*> is_used_vars;
|
||||
// Number of vehicle used
|
||||
is_used_vars.reserve(routing->vehicles());
|
||||
for (int v = 0; v < routing->vehicles(); v++) {
|
||||
is_used_vars.push_back(solver->MakeIsDifferentCstVar(
|
||||
routing->NextVar(routing->Start(v)), routing->End(v)));
|
||||
}
|
||||
solver->AddConstraint(
|
||||
solver->MakeEquality(solver->MakeSum(is_used_vars), used_vehicles));
|
||||
vars.push_back(used_vehicles);
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
// Outputs a solution to the current model in a std::string.
|
||||
std::string VerboseOutput(const RoutingModel& routing,
|
||||
const RoutingIndexManager& manager,
|
||||
const Assignment& assignment,
|
||||
const Coordinates& coords,
|
||||
const std::vector<int64>& service_times) {
|
||||
@@ -110,36 +145,39 @@ std::string VerboseOutput(const RoutingModel& routing,
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time");
|
||||
const RoutingDimension& load_dimension = routing.GetDimensionOrDie("demand");
|
||||
for (int i = 0; i < routing.vehicles(); ++i) {
|
||||
StringAppendF(&output, "Vehicle %d: ", i);
|
||||
absl::StrAppendFormat(&output, "Vehicle %d: ", i);
|
||||
int64 index = routing.Start(i);
|
||||
if (routing.IsEnd(assignment.Value(routing.NextVar(index)))) {
|
||||
output.append("empty");
|
||||
} else {
|
||||
while (!routing.IsEnd(index)) {
|
||||
RoutingModel::NodeIndex real_node = routing.IndexToNode(index);
|
||||
StringAppendF(&output, "%d ", real_node.value());
|
||||
absl::StrAppendFormat(&output, "%d ",
|
||||
manager.IndexToNode(index).value());
|
||||
const IntVar* vehicle = routing.VehicleVar(index);
|
||||
StringAppendF(&output, "Vehicle(%lld) ", assignment.Value(vehicle));
|
||||
absl::StrAppendFormat(&output, "Vehicle(%d) ",
|
||||
assignment.Value(vehicle));
|
||||
const IntVar* arrival = time_dimension.CumulVar(index);
|
||||
StringAppendF(&output, "Time(%lld..%lld) ", assignment.Min(arrival),
|
||||
assignment.Max(arrival));
|
||||
absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival),
|
||||
assignment.Max(arrival));
|
||||
const IntVar* load = load_dimension.CumulVar(index);
|
||||
StringAppendF(&output, "Load(%lld..%lld) ", assignment.Min(load),
|
||||
assignment.Max(load));
|
||||
index = assignment.Value(routing.NextVar(index));
|
||||
StringAppendF(&output, "Transit(%lld) ",
|
||||
TravelPlusServiceTime(&coords, &service_times, real_node,
|
||||
routing.IndexToNode(index)));
|
||||
absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load),
|
||||
assignment.Max(load));
|
||||
const int64 next_index = assignment.Value(routing.NextVar(index));
|
||||
absl::StrAppendFormat(
|
||||
&output, "Transit(%d) ",
|
||||
TravelPlusServiceTime(manager, &coords, &service_times, index,
|
||||
next_index));
|
||||
index = next_index;
|
||||
}
|
||||
output.append("Route end ");
|
||||
const IntVar* vehicle = routing.VehicleVar(index);
|
||||
StringAppendF(&output, "Vehicle(%lld) ", assignment.Value(vehicle));
|
||||
absl::StrAppendFormat(&output, "Vehicle(%d) ", assignment.Value(vehicle));
|
||||
const IntVar* arrival = time_dimension.CumulVar(index);
|
||||
StringAppendF(&output, "Time(%lld..%lld) ", assignment.Min(arrival),
|
||||
assignment.Max(arrival));
|
||||
absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival),
|
||||
assignment.Max(arrival));
|
||||
const IntVar* load = load_dimension.CumulVar(index);
|
||||
StringAppendF(&output, "Load(%lld..%lld) ", assignment.Min(load),
|
||||
assignment.Max(load));
|
||||
absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load),
|
||||
assignment.Max(load));
|
||||
}
|
||||
output.append("\n");
|
||||
}
|
||||
@@ -152,24 +190,19 @@ namespace {
|
||||
// parsed.
|
||||
bool SafeParseInt64Array(const std::string& str,
|
||||
std::vector<int64>* parsed_int) {
|
||||
static const char kWhiteSpaces[] = " \t\n\v\f\r";
|
||||
std::vector<std::string> items = absl::StrSplit(
|
||||
str, absl::delimiter::AnyOf(kWhiteSpaces), absl::SkipEmpty());
|
||||
parsed_int->assign(items.size(), 0);
|
||||
for (int i = 0; i < items.size(); ++i) {
|
||||
const char* item = items[i].c_str();
|
||||
char* endptr = nullptr;
|
||||
(*parsed_int)[i] = strto64(item, &endptr, 10);
|
||||
// The whole item should have been consumed.
|
||||
if (*endptr != '\0') return false;
|
||||
}
|
||||
return true;
|
||||
std::istringstream input(str);
|
||||
int64 x;
|
||||
parsed_int->clear();
|
||||
while (input >> x) parsed_int->push_back(x);
|
||||
return input.eof();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Builds and solves a model from a file in the format defined by Li & Lim
|
||||
// (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/).
|
||||
bool LoadAndSolve(const std::string& pdp_file) {
|
||||
bool LoadAndSolve(const std::string& pdp_file,
|
||||
const RoutingModelParameters& model_parameters,
|
||||
const RoutingSearchParameters& search_parameters) {
|
||||
// Load all the lines of the file in RAM (it shouldn't be too large anyway).
|
||||
std::vector<std::string> lines;
|
||||
{
|
||||
@@ -207,10 +240,10 @@ bool LoadAndSolve(const std::string& pdp_file) {
|
||||
std::vector<int64> open_times;
|
||||
std::vector<int64> close_times;
|
||||
std::vector<int64> service_times;
|
||||
std::vector<RoutingModel::NodeIndex> pickups;
|
||||
std::vector<RoutingModel::NodeIndex> deliveries;
|
||||
std::vector<RoutingIndexManager::NodeIndex> pickups;
|
||||
std::vector<RoutingIndexManager::NodeIndex> deliveries;
|
||||
int64 horizon = 0;
|
||||
RoutingModel::NodeIndex depot(0);
|
||||
RoutingIndexManager::NodeIndex depot(0);
|
||||
for (int line_index = 1; line_index < lines.size(); ++line_index) {
|
||||
if (!SafeParseInt64Array(lines[line_index], &parsed_int) ||
|
||||
parsed_int.size() != 9 || parsed_int[0] < 0 || parsed_int[4] < 0 ||
|
||||
@@ -235,68 +268,119 @@ bool LoadAndSolve(const std::string& pdp_file) {
|
||||
open_times.push_back(open_time);
|
||||
close_times.push_back(close_time);
|
||||
service_times.push_back(service_time);
|
||||
pickups.push_back(RoutingModel::NodeIndex(pickup));
|
||||
deliveries.push_back(RoutingModel::NodeIndex(delivery));
|
||||
pickups.push_back(RoutingIndexManager::NodeIndex(pickup));
|
||||
deliveries.push_back(RoutingIndexManager::NodeIndex(delivery));
|
||||
if (pickup == 0 && delivery == 0) {
|
||||
depot = RoutingModel::NodeIndex(pickups.size() - 1);
|
||||
depot = RoutingIndexManager::NodeIndex(pickups.size() - 1);
|
||||
}
|
||||
horizon = std::max(horizon, close_time);
|
||||
}
|
||||
|
||||
// Build pickup and delivery model.
|
||||
const int num_nodes = customer_ids.size();
|
||||
RoutingModelParameters model_parameters = BuildModelParametersFromFlags();
|
||||
RoutingModel routing(num_nodes, num_vehicles, depot, model_parameters);
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(Travel, const_cast<const Coordinates*>(&coords)));
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&Demand,
|
||||
const_cast<const std::vector<int64>*>(&demands)),
|
||||
0, capacity, /*fix_start_cumul_to_zero=*/true, "demand");
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(
|
||||
&TravelPlusServiceTime, const_cast<const Coordinates*>(&coords),
|
||||
const_cast<const std::vector<int64>*>(&service_times)),
|
||||
kScalingFactor * horizon, kScalingFactor * horizon,
|
||||
/*fix_start_cumul_to_zero=*/true, "time");
|
||||
RoutingIndexManager manager(num_nodes, num_vehicles, depot);
|
||||
RoutingModel routing(manager, model_parameters);
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&coords, &manager](int64 i, int64 j) {
|
||||
return Travel(const_cast<const Coordinates*>(&coords),
|
||||
manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
RoutingTransitCallback2 demand_evaluator = [&](int64 from_index,
|
||||
int64 to_index) {
|
||||
return demands[manager.IndexToNode(from_index).value()];
|
||||
};
|
||||
routing.AddDimension(routing.RegisterTransitCallback(demand_evaluator), 0,
|
||||
capacity, /*fix_start_cumul_to_zero=*/true, "demand");
|
||||
RoutingTransitCallback2 time_evaluator = [&](int64 from_index,
|
||||
int64 to_index) {
|
||||
return TravelPlusServiceTime(manager, &coords, &service_times, from_index,
|
||||
to_index);
|
||||
};
|
||||
routing.AddDimension(routing.RegisterTransitCallback(time_evaluator),
|
||||
kScalingFactor * horizon, kScalingFactor * horizon,
|
||||
/*fix_start_cumul_to_zero=*/true, "time");
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time");
|
||||
Solver* const solver = routing.solver();
|
||||
for (RoutingModel::NodeIndex i(0); i < num_nodes; ++i) {
|
||||
const int64 index = routing.NodeToIndex(i);
|
||||
if (pickups[i.value()] == 0 && deliveries[i.value()] != 0) {
|
||||
const int64 delivery_index = routing.NodeToIndex(deliveries[i.value()]);
|
||||
for (int node = 0; node < num_nodes; ++node) {
|
||||
const int64 index =
|
||||
manager.NodeToIndex(RoutingIndexManager::NodeIndex(node));
|
||||
if (pickups[node] == 0 && deliveries[node] != 0) {
|
||||
const int64 delivery_index = manager.NodeToIndex(deliveries[node]);
|
||||
solver->AddConstraint(solver->MakeEquality(
|
||||
routing.VehicleVar(index), routing.VehicleVar(delivery_index)));
|
||||
solver->AddConstraint(
|
||||
solver->MakeLessOrEqual(time_dimension.CumulVar(index),
|
||||
time_dimension.CumulVar(delivery_index)));
|
||||
routing.AddPickupAndDelivery(i, deliveries[i.value()]);
|
||||
routing.AddPickupAndDelivery(index,
|
||||
manager.NodeToIndex(deliveries[node]));
|
||||
}
|
||||
IntVar* const cumul = time_dimension.CumulVar(index);
|
||||
cumul->SetMin(kScalingFactor * open_times[i.value()]);
|
||||
cumul->SetMax(kScalingFactor * close_times[i.value()]);
|
||||
cumul->SetMin(kScalingFactor * open_times[node]);
|
||||
cumul->SetMax(kScalingFactor * close_times[node]);
|
||||
}
|
||||
|
||||
if (search_parameters.local_search_metaheuristic() ==
|
||||
LocalSearchMetaheuristic::GENERIC_TABU_SEARCH) {
|
||||
// Create variable for the total schedule time of the solution.
|
||||
// This will be used as one of the Tabu criteria.
|
||||
// This is done here and not in GetTabuVarsCallback as it requires calling
|
||||
// AddVariableMinimizedByFinalizer and this method must be called early.
|
||||
std::vector<IntVar*> end_cumuls;
|
||||
std::vector<IntVar*> start_cumuls;
|
||||
for (int i = 0; i < routing.vehicles(); ++i) {
|
||||
end_cumuls.push_back(time_dimension.CumulVar(routing.End(i)));
|
||||
start_cumuls.push_back(time_dimension.CumulVar(routing.Start(i)));
|
||||
}
|
||||
IntVar* total_time = solver->MakeIntVar(0, 99999999, "total");
|
||||
solver->AddConstraint(solver->MakeEquality(
|
||||
solver->MakeDifference(solver->MakeSum(end_cumuls),
|
||||
solver->MakeSum(start_cumuls)),
|
||||
total_time));
|
||||
|
||||
routing.AddVariableMinimizedByFinalizer(total_time);
|
||||
|
||||
RoutingModel::GetTabuVarsCallback tabu_var_callback =
|
||||
[total_time](RoutingModel* model) {
|
||||
return GetTabuVars({total_time}, model);
|
||||
};
|
||||
routing.SetTabuVarsCallback(tabu_var_callback);
|
||||
}
|
||||
|
||||
// Adding penalty costs to allow skipping orders.
|
||||
const int64 kPenalty = 10000000;
|
||||
for (RoutingModel::NodeIndex order(1); order < routing.nodes(); ++order) {
|
||||
std::vector<RoutingModel::NodeIndex> orders(1, order);
|
||||
for (RoutingIndexManager::NodeIndex order(1); order < routing.nodes();
|
||||
++order) {
|
||||
std::vector<int64> orders(1, manager.NodeToIndex(order));
|
||||
routing.AddDisjunction(orders, kPenalty);
|
||||
}
|
||||
|
||||
// Set up search parameters.
|
||||
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.SolveWithParameters(parameters);
|
||||
SimpleCycleTimer timer;
|
||||
timer.Start();
|
||||
const Assignment* assignment = routing.SolveWithParameters(search_parameters);
|
||||
timer.Stop();
|
||||
LOG(INFO) << routing.solver()->LocalSearchProfile();
|
||||
if (nullptr != assignment) {
|
||||
LOG(INFO) << VerboseOutput(routing, manager, *assignment, coords,
|
||||
service_times);
|
||||
LOG(INFO) << "Cost: " << assignment->ObjectiveValue();
|
||||
LOG(INFO) << VerboseOutput(routing, *assignment, coords, service_times);
|
||||
int skipped_nodes = 0;
|
||||
for (int node = 0; node < routing.Size(); node++) {
|
||||
if (!routing.IsEnd(node) && !routing.IsStart(node) &&
|
||||
assignment->Value(routing.NextVar(node)) == node) {
|
||||
skipped_nodes++;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Number of skipped nodes: " << skipped_nodes;
|
||||
int num_used_vehicles = 0;
|
||||
for (int v = 0; v < routing.vehicles(); v++) {
|
||||
if (routing.IsVehicleUsed(*assignment, v)) {
|
||||
num_used_vehicles++;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Number of used vehicles: " << num_used_vehicles;
|
||||
LOG(INFO) << "Time: " << timer.Get();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -305,9 +389,18 @@ bool LoadAndSolve(const std::string& pdp_file) {
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
base::SetFlag(&FLAGS_logtostderr, true);
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (!operations_research::LoadAndSolve(FLAGS_pdp_file)) {
|
||||
operations_research::RoutingModelParameters model_parameters =
|
||||
operations_research::DefaultRoutingModelParameters();
|
||||
model_parameters.set_reduce_vehicle_cost_model(
|
||||
FLAGS_reduce_vehicle_cost_model);
|
||||
operations_research::RoutingSearchParameters search_parameters =
|
||||
operations_research::DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, &search_parameters));
|
||||
if (!operations_research::LoadAndSolve(FLAGS_pdp_file, model_parameters,
|
||||
search_parameters)) {
|
||||
LOG(INFO) << "Error solving " << FLAGS_pdp_file;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/status.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/graph/ebert_graph.h"
|
||||
#include "ortools/graph/linear_assignment.h"
|
||||
|
||||
@@ -46,13 +46,13 @@ void PrintDimacsAssignmentProblem(
|
||||
CHECK_OK(file::Open(output_filename, "w", &output, file::Defaults()));
|
||||
const GraphType& graph(assignment.Graph());
|
||||
std::string output_line =
|
||||
StringPrintf("p asn %d %d\n", graph.num_nodes(), graph.num_arcs());
|
||||
absl::StrFormat("p asn %d %d\n", graph.num_nodes(), graph.num_arcs());
|
||||
CHECK_OK(file::WriteString(output, output_line, file::Defaults()));
|
||||
|
||||
for (typename LinearSumAssignment<GraphType>::BipartiteLeftNodeIterator
|
||||
node_it(assignment);
|
||||
node_it.Ok(); node_it.Next()) {
|
||||
output_line = StringPrintf("n %d\n", node_it.Index() + 1);
|
||||
output_line = absl::StrFormat("n %d\n", node_it.Index() + 1);
|
||||
CHECK_OK(file::WriteString(output, output_line, file::Defaults()));
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ void PrintDimacsAssignmentProblem(
|
||||
for (typename GraphType::ArcIterator arc_it(assignment.Graph()); arc_it.Ok();
|
||||
arc_it.Next()) {
|
||||
ArcIndex arc = arc_it.Index();
|
||||
output_line = StringPrintf("a %d %d %lld\n", graph.Tail(arc) + 1,
|
||||
graph.Head(arc) + 1, assignment.ArcCost(arc));
|
||||
output_line = absl::StrFormat("a %d %d %d\n", graph.Tail(arc) + 1,
|
||||
graph.Head(arc) + 1, assignment.ArcCost(arc));
|
||||
CHECK_OK(file::WriteString(output, output_line, file::Defaults()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,15 +27,17 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/callback.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/join.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_enums.pb.h"
|
||||
#include "ortools/constraint_solver/routing_flags.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
||||
|
||||
DEFINE_int32(tsp_size, 10, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(tsp_use_random_matrix, true, "Use random cost matrix.");
|
||||
@@ -43,6 +45,13 @@ DEFINE_int32(tsp_random_forbidden_connections, 0,
|
||||
"Number of random forbidden connections.");
|
||||
DEFINE_bool(tsp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_string(routing_search_parameters,
|
||||
"local_search_operators {"
|
||||
" use_path_lns:BOOL_TRUE"
|
||||
" use_inactive_lns:BOOL_TRUE"
|
||||
"}",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -58,7 +67,8 @@ int32 GetSeed() {
|
||||
// Cost/distance functions.
|
||||
|
||||
// Sample function.
|
||||
int64 MyDistance(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) {
|
||||
int64 MyDistance(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) {
|
||||
// Put your distance code here.
|
||||
return (from + to).value(); // for instance
|
||||
}
|
||||
@@ -68,13 +78,11 @@ class RandomMatrix {
|
||||
public:
|
||||
explicit RandomMatrix(int size) : size_(size) {}
|
||||
void Initialize() {
|
||||
matrix_.reset(new int64[size_ * size_]);
|
||||
matrix_ = absl::make_unique<int64[]>(size_ * size_);
|
||||
const int64 kDistanceMax = 100;
|
||||
ACMRandom randomizer(GetSeed());
|
||||
for (RoutingModel::NodeIndex from = RoutingModel::kFirstNode; from < size_;
|
||||
++from) {
|
||||
for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_;
|
||||
++to) {
|
||||
for (RoutingIndexManager::NodeIndex from(0); from < size_; ++from) {
|
||||
for (RoutingIndexManager::NodeIndex to(0); to < size_; ++to) {
|
||||
if (to != from) {
|
||||
matrix_[MatrixIndex(from, to)] = randomizer.Uniform(kDistanceMax);
|
||||
} else {
|
||||
@@ -83,14 +91,14 @@ class RandomMatrix {
|
||||
}
|
||||
}
|
||||
}
|
||||
int64 Distance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
int64 Distance(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const {
|
||||
return matrix_[MatrixIndex(from, to)];
|
||||
}
|
||||
|
||||
private:
|
||||
int64 MatrixIndex(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
int64 MatrixIndex(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const {
|
||||
return (from * size_ + to).value();
|
||||
}
|
||||
std::unique_ptr<int64[]> matrix_;
|
||||
@@ -103,11 +111,12 @@ void Tsp() {
|
||||
// Second argument = 1 to build a single tour (it's a TSP).
|
||||
// Nodes are indexed from 0 to FLAGS_tsp_size - 1, by default the start of
|
||||
// the route is node 0.
|
||||
RoutingModel routing(FLAGS_tsp_size, 1, RoutingModel::NodeIndex(0));
|
||||
RoutingSearchParameters parameters = BuildSearchParametersFromFlags();
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
parameters.set_first_solution_strategy(
|
||||
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
RoutingIndexManager manager(FLAGS_tsp_size, 1,
|
||||
RoutingIndexManager::NodeIndex(0));
|
||||
RoutingModel routing(manager);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
|
||||
// Setting the cost function.
|
||||
// Put a permanent callback to the distance accessor here. The callback
|
||||
@@ -116,11 +125,18 @@ void Tsp() {
|
||||
RandomMatrix matrix(FLAGS_tsp_size);
|
||||
if (FLAGS_tsp_use_random_matrix) {
|
||||
matrix.Initialize();
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&matrix, &RandomMatrix::Distance));
|
||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
||||
[&matrix, &manager](int64 i, int64 j) {
|
||||
return matrix.Distance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
} else {
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(MyDistance));
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&manager](int64 i, int64 j) {
|
||||
return MyDistance(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
}
|
||||
// Forbid node connections (randomly).
|
||||
ACMRandom randomizer(GetSeed());
|
||||
@@ -145,11 +161,11 @@ void Tsp() {
|
||||
std::string route;
|
||||
for (int64 node = routing.Start(route_number); !routing.IsEnd(node);
|
||||
node = solution->Value(routing.NextVar(node))) {
|
||||
absl::StrAppend(&route, routing.IndexToNode(node).value(), " (", node,
|
||||
absl::StrAppend(&route, manager.IndexToNode(node).value(), " (", node,
|
||||
") -> ");
|
||||
}
|
||||
const int64 end = routing.End(route_number);
|
||||
absl::StrAppend(&route, routing.IndexToNode(end).value(), " (", end, ")");
|
||||
absl::StrAppend(&route, manager.IndexToNode(end).value(), " (", end, ")");
|
||||
LOG(INFO) << route;
|
||||
} else {
|
||||
LOG(INFO) << "No solution found.";
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/filelineiter.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/macros.h"
|
||||
#include "ortools/base/span.h"
|
||||
#include "ortools/base/split.h"
|
||||
#include "ortools/base/string_view.h"
|
||||
#include "ortools/base/strtoint.h"
|
||||
#include "ortools/sat/boolean_problem.pb.h"
|
||||
#include "ortools/sat/cp_model.pb.h"
|
||||
@@ -36,8 +36,6 @@ DEFINE_bool(wcnf_use_strong_slack, true,
|
||||
"If true, when we add a slack variable to reify a soft clause, we "
|
||||
"enforce the fact that when it is true, the clause must be false.");
|
||||
|
||||
using ::absl::delimiter::AnyOf;
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
@@ -49,7 +47,7 @@ struct LinearBooleanProblemWrapper {
|
||||
problem->set_original_num_variables(num);
|
||||
}
|
||||
|
||||
void AddConstraint(absl::Span<int> clause) {
|
||||
void AddConstraint(absl::Span<const int> clause) {
|
||||
LinearBooleanConstraint* constraint = problem->add_constraints();
|
||||
constraint->mutable_literals()->Reserve(clause.size());
|
||||
constraint->mutable_coefficients()->Reserve(clause.size());
|
||||
@@ -94,7 +92,7 @@ struct CpModelProtoWrapper {
|
||||
return signed_value > 0 ? signed_value - 1 : signed_value;
|
||||
}
|
||||
|
||||
void AddConstraint(absl::Span<int> clause) {
|
||||
void AddConstraint(absl::Span<const int> clause) {
|
||||
auto* constraint = problem->add_constraints()->mutable_bool_or();
|
||||
constraint->mutable_literals()->Reserve(clause.size());
|
||||
for (const int literal : clause) {
|
||||
@@ -233,8 +231,7 @@ class SatCnfReader {
|
||||
}
|
||||
|
||||
static const char kWordDelimiters[] = " ";
|
||||
auto splitter = absl::StrSplit(line, kWordDelimiters,
|
||||
static_cast<int64>(absl::SkipEmpty()));
|
||||
auto splitter = absl::StrSplit(line, kWordDelimiters, absl::SkipEmpty());
|
||||
|
||||
tmp_clause_.clear();
|
||||
int64 weight = (!is_wcnf_ && interpret_cnf_as_max_sat_) ? 1 : hard_weight_;
|
||||
@@ -312,7 +309,7 @@ class SatCnfReader {
|
||||
int num_variables_;
|
||||
|
||||
// Temporary storage for ProcessNewLine().
|
||||
std::vector<std::string> words_;
|
||||
std::vector<absl::string_view> words_;
|
||||
|
||||
// We stores the objective in a map because we want the variables to appear
|
||||
// only once in the LinearObjective proto.
|
||||
|
||||
@@ -19,18 +19,20 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "examples/cpp/opb_reader.h"
|
||||
#include "examples/cpp/sat_cnf_reader.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/algorithms/sparse_permutation.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/int_type.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/join.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/status.h"
|
||||
#include "ortools/base/stringpiece_utils.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/base/strtoint.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/sat/boolean_problem.h"
|
||||
@@ -138,18 +140,18 @@ double GetScaledTrivialBestBound(const LinearBooleanProblem& problem) {
|
||||
return AddOffsetAndScaleObjectiveValue(problem, best_bound);
|
||||
}
|
||||
|
||||
void LoadBooleanProblem(const std::string& filename,
|
||||
bool LoadBooleanProblem(const std::string& filename,
|
||||
LinearBooleanProblem* problem, CpModelProto* cp_model) {
|
||||
if (strings::EndsWith(filename, ".opb") ||
|
||||
strings::EndsWith(filename, ".opb.bz2")) {
|
||||
if (absl::EndsWith(filename, ".opb") ||
|
||||
absl::EndsWith(filename, ".opb.bz2")) {
|
||||
OpbReader reader;
|
||||
if (!reader.Load(filename, problem)) {
|
||||
LOG(FATAL) << "Cannot load file '" << filename << "'.";
|
||||
}
|
||||
} else if (strings::EndsWith(filename, ".cnf") ||
|
||||
strings::EndsWith(filename, ".cnf.gz") ||
|
||||
strings::EndsWith(filename, ".wcnf") ||
|
||||
strings::EndsWith(filename, ".wcnf.gz")) {
|
||||
} else if (absl::EndsWith(filename, ".cnf") ||
|
||||
absl::EndsWith(filename, ".cnf.gz") ||
|
||||
absl::EndsWith(filename, ".wcnf") ||
|
||||
absl::EndsWith(filename, ".wcnf.gz")) {
|
||||
SatCnfReader reader;
|
||||
if (FLAGS_fu_malik || FLAGS_linear_scan || FLAGS_wpm1 || FLAGS_qmaxsat ||
|
||||
FLAGS_core_enc) {
|
||||
@@ -171,6 +173,7 @@ void LoadBooleanProblem(const std::string& filename,
|
||||
LOG(INFO) << "Reading a LinearBooleanProblem.";
|
||||
*problem = ReadFileToProtoOrDie<LinearBooleanProblem>(filename);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SolutionString(const LinearBooleanProblem& problem,
|
||||
@@ -210,7 +213,12 @@ int Run() {
|
||||
// Read the problem.
|
||||
LinearBooleanProblem problem;
|
||||
CpModelProto cp_model;
|
||||
LoadBooleanProblem(FLAGS_input, &problem, &cp_model);
|
||||
if (!LoadBooleanProblem(FLAGS_input, &problem, &cp_model)) {
|
||||
CpSolverResponse response;
|
||||
response.set_status(CpSolverStatus::MODEL_INVALID);
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (FLAGS_use_cp_model && cp_model.variables_size() == 0) {
|
||||
LOG(INFO) << "Converting to CpModelProto ...";
|
||||
cp_model = BooleanProblemToCpModelproto(problem);
|
||||
@@ -231,7 +239,7 @@ int Run() {
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
|
||||
if (!FLAGS_output.empty()) {
|
||||
if (strings::EndsWith(FLAGS_output, ".txt")) {
|
||||
if (absl::EndsWith(FLAGS_output, ".txt")) {
|
||||
CHECK_OK(file::SetTextProto(FLAGS_output, response, file::Defaults()));
|
||||
} else {
|
||||
CHECK_OK(
|
||||
@@ -317,7 +325,7 @@ int Run() {
|
||||
}
|
||||
if (result == SatSolver::LIMIT_REACHED) {
|
||||
if (FLAGS_qmaxsat) {
|
||||
solver.reset(new SatSolver());
|
||||
solver = absl::make_unique<SatSolver>();
|
||||
solver->SetParameters(parameters);
|
||||
CHECK(LoadBooleanProblem(problem, solver.get()));
|
||||
result = SolveWithCardinalityEncoding(STDOUT_LOG, problem, solver.get(),
|
||||
@@ -381,7 +389,7 @@ int Run() {
|
||||
if (result == SatSolver::FEASIBLE) {
|
||||
StoreAssignment(solver->Assignment(), problem.mutable_assignment());
|
||||
}
|
||||
if (strings::EndsWith(FLAGS_output, ".txt")) {
|
||||
if (absl::EndsWith(FLAGS_output, ".txt")) {
|
||||
CHECK_OK(file::SetTextProto(FLAGS_output, problem, file::Defaults()));
|
||||
} else {
|
||||
CHECK_OK(file::SetBinaryProto(FLAGS_output, problem, file::Defaults()));
|
||||
@@ -408,9 +416,9 @@ int Run() {
|
||||
|
||||
// Print final statistics.
|
||||
printf("c booleans: %d\n", solver->NumVariables());
|
||||
printf("c conflicts: %lld\n", solver->num_failures());
|
||||
printf("c branches: %lld\n", solver->num_branches());
|
||||
printf("c propagations: %lld\n", solver->num_propagations());
|
||||
absl::PrintF("c conflicts: %d\n", solver->num_failures());
|
||||
absl::PrintF("c branches: %d\n", solver->num_branches());
|
||||
absl::PrintF("c propagations: %d\n", solver->num_propagations());
|
||||
printf("c walltime: %f\n", wall_timer.Get());
|
||||
printf("c usertime: %f\n", user_timer.Get());
|
||||
printf("c deterministic_time: %f\n", solver->deterministic_time());
|
||||
@@ -434,9 +442,9 @@ int main(int argc, char** argv) {
|
||||
// By default, we want to show how the solver progress. Note that this needs
|
||||
// to be set before InitGoogle() which has the nice side-effect of allowing
|
||||
// the user to override it.
|
||||
// base::SetFlag(&FLAGS_vmodule, "*cp_model*=1");
|
||||
absl::SetFlag(&FLAGS_vmodule, "*cp_model*=1");
|
||||
gflags::SetUsageMessage(kUsage);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
base::SetFlag(&FLAGS_alsologtostderr, true);
|
||||
absl::SetFlag(&FLAGS_alsologtostderr, true);
|
||||
return operations_research::sat::Run();
|
||||
}
|
||||
|
||||
@@ -26,16 +26,15 @@
|
||||
// - The objective it to minimize the number of active workers, while
|
||||
// performing all the jobs.
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/filelineiter.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/split.h"
|
||||
#include "ortools/base/strtoint.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
#include "ortools/sat/model.h"
|
||||
@@ -108,7 +107,7 @@ class ShiftMinimizationParser {
|
||||
}
|
||||
|
||||
const std::vector<std::string> words =
|
||||
absl::StrSplit(line, absl::delimiter::AnyOf(" :\t"), absl::SkipEmpty());
|
||||
absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty());
|
||||
|
||||
switch (load_status_) {
|
||||
case NOT_STARTED: {
|
||||
@@ -296,7 +295,7 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
base::SetFlag(&FLAGS_logtostderr, true);
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_input.empty()) {
|
||||
LOG(FATAL) << "Please supply a data file with --input=";
|
||||
|
||||
@@ -18,26 +18,24 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/base/timer.h"
|
||||
//#include "ortools/base/options.h"
|
||||
#include "ortools/base/stringpiece_utils.h"
|
||||
#include "ortools/linear_solver/linear_solver.h"
|
||||
#include "ortools/linear_solver/linear_solver.pb.h"
|
||||
//#include "ortools/linear_solver/model_anonymizer.h"
|
||||
#include "ortools/lp_data/lp_data.h"
|
||||
#include "ortools/lp_data/mps_reader.h"
|
||||
#include "ortools/lp_data/model_reader.h"
|
||||
#include "ortools/lp_data/proto_utils.h"
|
||||
#include "ortools/util/file_util.h"
|
||||
|
||||
DEFINE_string(input, "", "REQUIRED: Input file name.");
|
||||
DEFINE_string(solver, "glop",
|
||||
"The solver to use: bop, cbc, clp, glop, glpk_lp, glpk_mip, "
|
||||
"gurobi_lp, gurobi_mip, scip, knapsack.");
|
||||
"gurobi_lp, gurobi_mip, scip, knapsack, sat.");
|
||||
|
||||
DEFINE_string(params_file, "",
|
||||
"Solver specific parameters file. "
|
||||
@@ -53,10 +51,6 @@ DEFINE_string(output_csv, "",
|
||||
"If non-empty, write the returned solution in csv format with "
|
||||
"each line formed by a variable name and its value.");
|
||||
|
||||
// DEFINE_bool(anonymize, false,
|
||||
// "Whether to anonymize model before solving. Useful if model, "
|
||||
// "request, and/or response is dumped to a file.");
|
||||
|
||||
DEFINE_string(dump_format, "text",
|
||||
"Format in which to dump protos (if flags --dump_model, "
|
||||
"--dump_request, or --dump_response are used). Possible values: "
|
||||
@@ -86,87 +80,22 @@ namespace {
|
||||
void Run() {
|
||||
// Create the solver and set its parameters.
|
||||
MPSolver::OptimizationProblemType type;
|
||||
if (FLAGS_solver == "glop") {
|
||||
type = MPSolver::GLOP_LINEAR_PROGRAMMING;
|
||||
#if defined(USE_GLPK)
|
||||
} else if (FLAGS_solver == "glpk_lp") {
|
||||
type = MPSolver::GLPK_LINEAR_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_CLP)
|
||||
} else if (FLAGS_solver == "clp") {
|
||||
type = MPSolver::CLP_LINEAR_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_CPLEX)
|
||||
} else if (FLAGS_solver == "cplex") {
|
||||
type = MPSolver::CPLEX_LINEAR_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_GUROBI)
|
||||
} else if (FLAGS_solver == "gurobi_lp") {
|
||||
type = MPSolver::GUROBI_LINEAR_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_SCIP)
|
||||
} else if (FLAGS_solver == "scip") {
|
||||
type = MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_GUROBI)
|
||||
} else if (FLAGS_solver == "cbc") {
|
||||
type = MPSolver::CBC_MIXED_INTEGER_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_GLPK)
|
||||
} else if (FLAGS_solver == "glpk_mip") {
|
||||
type = MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_CPLEX)
|
||||
} else if (FLAGS_solver == "cplex_mip") {
|
||||
type = MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_GUROBI)
|
||||
} else if (FLAGS_solver == "gurobi_mip") {
|
||||
type = MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING;
|
||||
#endif
|
||||
#if defined(USE_BOP)
|
||||
} else if (FLAGS_solver == "bop") {
|
||||
type = MPSolver::BOP_INTEGER_PROGRAMMING;
|
||||
#endif
|
||||
} else {
|
||||
LOG(FATAL) << "Unsupported --solver: " << FLAGS_solver;
|
||||
}
|
||||
QCHECK(MPSolver::ParseSolverType(FLAGS_solver, &type))
|
||||
<< "Unsupported --solver: " << FLAGS_solver;
|
||||
|
||||
// Load the problem into an MPModelProto.
|
||||
MPModelProto model_proto;
|
||||
MPModelRequest request_proto;
|
||||
if (strings::EndsWith(FLAGS_input, ".mps") ||
|
||||
strings::EndsWith(FLAGS_input, ".mps.gz")) {
|
||||
glop::LinearProgram linear_program_fixed;
|
||||
glop::LinearProgram linear_program_free;
|
||||
glop::MPSReader mps_reader;
|
||||
mps_reader.set_log_errors(FLAGS_forced_mps_format == "free" ||
|
||||
FLAGS_forced_mps_format == "fixed");
|
||||
bool fixed_read =
|
||||
FLAGS_forced_mps_format != "free" &&
|
||||
mps_reader.LoadFileWithMode(FLAGS_input, false, &linear_program_fixed);
|
||||
const bool free_read =
|
||||
FLAGS_forced_mps_format != "fixed" &&
|
||||
mps_reader.LoadFileWithMode(FLAGS_input, true, &linear_program_free);
|
||||
CHECK(fixed_read || free_read)
|
||||
<< "Error while parsing the mps file '" << FLAGS_input << "' "
|
||||
<< "Use the --forced_mps_format flags to see the errors.";
|
||||
if (fixed_read && free_read) {
|
||||
if (linear_program_fixed.name() != linear_program_free.name()) {
|
||||
LOG(INFO) << "Name of the model differs between fixed and free forms. "
|
||||
<< "Fallbacking to free form.";
|
||||
fixed_read = false;
|
||||
}
|
||||
}
|
||||
if (!fixed_read) {
|
||||
LOG(INFO) << "Read file in free format.";
|
||||
LinearProgramToMPModelProto(linear_program_free, &model_proto);
|
||||
} else {
|
||||
LOG(INFO) << "Read file in fixed format.";
|
||||
LinearProgramToMPModelProto(linear_program_fixed, &model_proto);
|
||||
}
|
||||
if (absl::EndsWith(FLAGS_input, ".mps") ||
|
||||
absl::EndsWith(FLAGS_input, ".mps.gz")) {
|
||||
glop::LinearProgram linear_program;
|
||||
CHECK(glop::LoadLinearProgramFromMps(FLAGS_input, FLAGS_forced_mps_format,
|
||||
&linear_program))
|
||||
<< "Failed to parse mps file " << FLAGS_input;
|
||||
LinearProgramToMPModelProto(linear_program, &model_proto);
|
||||
} else {
|
||||
file::ReadFileToProto(FLAGS_input, &model_proto);
|
||||
file::ReadFileToProto(FLAGS_input, &request_proto);
|
||||
ReadFileToProto(FLAGS_input, &model_proto);
|
||||
ReadFileToProto(FLAGS_input, &request_proto);
|
||||
// If the input proto is in binary format, both ReadFileToProto could return
|
||||
// true. Instead use the actual number of variables found to test the
|
||||
// correct format of the input.
|
||||
@@ -185,10 +114,6 @@ void Run() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (FLAGS_anonymize) {
|
||||
// LOG(INFO) << "Anonymizing the model with autonumbers.";
|
||||
// operations_research::Anonymize(/*autonumber=*/true, &model_proto);
|
||||
// }
|
||||
printf("%-12s: '%s'\n", "File", FLAGS_input.c_str());
|
||||
|
||||
// Detect format to dump protos.
|
||||
@@ -249,11 +174,10 @@ void Run() {
|
||||
// Solve.
|
||||
MPSolverParameters param;
|
||||
MPSolver::ResultStatus solve_status = MPSolver::NOT_SOLVED;
|
||||
double solving_time_in_sec = 0;
|
||||
{
|
||||
ScopedWallTime timer(&solving_time_in_sec);
|
||||
solve_status = solver.Solve(param);
|
||||
}
|
||||
absl::Duration solving_time;
|
||||
const absl::Time time_before = absl::Now();
|
||||
solve_status = solver.Solve(param);
|
||||
solving_time = absl::Now() - time_before;
|
||||
|
||||
// If requested, re-create a corresponding MPModelRequest and save it to file.
|
||||
if (!FLAGS_dump_request.empty()) {
|
||||
@@ -283,9 +207,8 @@ void Run() {
|
||||
solver.FillSolutionResponseProto(&result);
|
||||
std::string csv_file;
|
||||
for (int i = 0; i < result.variable_value_size(); ++i) {
|
||||
csv_file +=
|
||||
StringPrintf("%s,%e\n", model_proto.variable(i).name().c_str(),
|
||||
result.variable_value(i));
|
||||
csv_file += absl::StrFormat("%s,%e\n", model_proto.variable(i).name(),
|
||||
result.variable_value(i));
|
||||
}
|
||||
CHECK_OK(file::SetContents(FLAGS_output_csv, csv_file, file::Defaults()));
|
||||
}
|
||||
@@ -304,12 +227,14 @@ void Run() {
|
||||
.c_str());
|
||||
printf("%-12s: %15.15e\n", "Objective",
|
||||
has_solution ? solver.Objective().Value() : 0.0);
|
||||
printf("%-12s: %lld\n", "Iterations", solver.iterations());
|
||||
printf("%-12s: %15.15e\n", "BestBound",
|
||||
has_solution ? solver.Objective().BestBound() : 0.0);
|
||||
absl::PrintF("%-12s: %d\n", "Iterations", solver.iterations());
|
||||
// NOTE(user): nodes() for non-MIP solvers crashes in debug mode by design.
|
||||
if (solver.IsMIP()) {
|
||||
printf("%-12s: %lld\n", "Nodes", solver.nodes());
|
||||
absl::PrintF("%-12s: %d\n", "Nodes", solver.nodes());
|
||||
}
|
||||
printf("%-12s: %-6.4g\n", "Time", solving_time_in_sec);
|
||||
printf("%-12s: %-6.4g\n", "Time", absl::ToDoubleSeconds(solving_time));
|
||||
}
|
||||
} // namespace
|
||||
} // namespace operations_research
|
||||
@@ -318,4 +243,6 @@ int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/true);
|
||||
CHECK(!FLAGS_input.empty()) << "--input is required";
|
||||
operations_research::Run();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -59,10 +59,10 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/macros.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/linear_solver/linear_solver.h"
|
||||
|
||||
DEFINE_bool(colgen_verbose, false, "print verbosely");
|
||||
@@ -275,8 +275,8 @@ class Box {
|
||||
}
|
||||
|
||||
std::string DebugString() const {
|
||||
return StringPrintf("[%d,%dx%d,%d]c%d", x_min(), y_min(), x_max(), y_max(),
|
||||
Cost());
|
||||
return absl::StrFormat("[%d,%dx%d,%d]c%d", x_min(), y_min(), x_max(),
|
||||
y_max(), Cost());
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -442,11 +442,11 @@ class CoveringProblem {
|
||||
|
||||
std::string PrintGrid() const {
|
||||
std::string output =
|
||||
StringPrintf("width = %d, height = %d, max_boxes = %d\n", width_,
|
||||
height_, max_boxes_);
|
||||
absl::StrFormat("width = %d, height = %d, max_boxes = %d\n", width_,
|
||||
height_, max_boxes_);
|
||||
for (int y = 0; y < height_; ++y) {
|
||||
StringAppendF(&output, "%s\n",
|
||||
std::string(grid_ + width_ * y, width_).c_str());
|
||||
absl::StrAppendFormat(&output, "%s\n",
|
||||
std::string(grid_ + width_ * y, width_));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@@ -458,7 +458,7 @@ class CoveringProblem {
|
||||
std::string PrintCovering() const {
|
||||
static const double kTolerance = 1e-5;
|
||||
std::string output =
|
||||
StringPrintf("cost = %lf\n", solver_->Objective().Value());
|
||||
absl::StrFormat("cost = %f\n", solver_->Objective().Value());
|
||||
std::unique_ptr<char[]> display(new char[(width_ + 1) * height_ + 1]);
|
||||
for (int y = 0; y < height_; ++y) {
|
||||
memcpy(display.get() + y * (width_ + 1), grid_ + width_ * y,
|
||||
@@ -473,8 +473,8 @@ class CoveringProblem {
|
||||
const char box_character =
|
||||
(i->second->solution_value() >= (1. - kTolerance) ? 'A' : 'a') +
|
||||
active_box_index++;
|
||||
StringAppendF(&output, "%c: box %s with value %lf\n", box_character,
|
||||
i->first.DebugString().c_str(), value);
|
||||
absl::StrAppendFormat(&output, "%c: box %s with value %f\n",
|
||||
box_character, i->first.DebugString(), value);
|
||||
const Box& box = i->first;
|
||||
for (int x = box.x_min(); x <= box.x_max(); ++x) {
|
||||
for (int y = box.y_min(); y <= box.y_max(); ++y) {
|
||||
@@ -606,18 +606,18 @@ int main(int argc, char** argv) {
|
||||
|
||||
operations_research::MPSolver::OptimizationProblemType solver_type;
|
||||
bool found = false;
|
||||
#if defined(USE_GLOP)
|
||||
if (FLAGS_colgen_solver == "glop") {
|
||||
solver_type = operations_research::MPSolver::GLOP_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif // USE_GLOP
|
||||
#if defined(USE_CLP)
|
||||
if (FLAGS_colgen_solver == "clp") {
|
||||
solver_type = operations_research::MPSolver::CLP_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif // USE_CLP
|
||||
#if defined(USE_GLOP)
|
||||
if (FLAGS_colgen_solver == "glop") {
|
||||
solver_type = operations_research::MPSolver::GLOP_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif // USE_GLOP
|
||||
if (!found) {
|
||||
LOG(ERROR) << "Unknown solver " << FLAGS_colgen_solver;
|
||||
return 1;
|
||||
|
||||
@@ -11,130 +11,139 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
|
||||
namespace operations_research {
|
||||
class DataProblem {
|
||||
private:
|
||||
std::vector<std::vector<int>> locations_;
|
||||
class DataProblem {
|
||||
private:
|
||||
std::vector<std::vector<int>> locations_;
|
||||
|
||||
public:
|
||||
DataProblem() {
|
||||
locations_ = {{4, 4}, {2, 0}, {8, 0}, {0, 1}, {1, 1}, {5, 2},
|
||||
{7, 2}, {3, 3}, {6, 3}, {5, 5}, {8, 5}, {1, 6},
|
||||
{2, 6}, {3, 7}, {6, 7}, {0, 8}, {7, 8}};
|
||||
public:
|
||||
DataProblem() {
|
||||
locations_ = {
|
||||
{4, 4},
|
||||
{2, 0}, {8, 0},
|
||||
{0, 1}, {1, 1},
|
||||
{5, 2}, {7, 2},
|
||||
{3, 3}, {6, 3},
|
||||
{5, 5}, {8, 5},
|
||||
{1, 6}, {2, 6},
|
||||
{3, 7}, {6, 7},
|
||||
{0, 8}, {7, 8}
|
||||
};
|
||||
|
||||
// Compute locations in meters using the block dimension defined as follow
|
||||
// Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
// here we use: 114m x 80m city block
|
||||
// src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
|
||||
std::array<int, 2> cityBlock = {228 / 2, 80};
|
||||
for (auto& i : locations_) {
|
||||
i[0] = i[0] * cityBlock[0];
|
||||
i[1] = i[1] * cityBlock[1];
|
||||
}
|
||||
}
|
||||
// Compute locations in meters using the block dimension defined as follow
|
||||
// Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
// here we use: 114m x 80m city block
|
||||
// src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
|
||||
std::array<int, 2> cityBlock = {228/2, 80};
|
||||
for (auto &i: locations_) {
|
||||
i[0] = i[0] * cityBlock[0];
|
||||
i[1] = i[1] * cityBlock[1];
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t GetVehicleNumber() const { return 1; }
|
||||
const std::vector<std::vector<int>>& GetLocations() const {
|
||||
return locations_;
|
||||
}
|
||||
RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode; }
|
||||
};
|
||||
std::size_t GetVehicleNumber() const { return 1;}
|
||||
const std::vector<std::vector<int>>& GetLocations() const { return locations_;}
|
||||
RoutingIndexManager::NodeIndex GetDepot() const { return RoutingIndexManager::NodeIndex(0);}
|
||||
};
|
||||
|
||||
/*! @brief Manhattan distance implemented as a callback.
|
||||
* @details It uses an array of positions and
|
||||
* computes the Manhattan distance between the two positions of two different
|
||||
* indices.*/
|
||||
class ManhattanDistance : public RoutingModel::NodeEvaluator2 {
|
||||
private:
|
||||
std::vector<std::vector<int64>> distances_;
|
||||
|
||||
public:
|
||||
ManhattanDistance(const DataProblem& data) {
|
||||
// Precompute distance between location to have distance callback in O(1)
|
||||
distances_ = std::vector<std::vector<int64>>(
|
||||
data.GetLocations().size(),
|
||||
std::vector<int64>(data.GetLocations().size(), 0LL));
|
||||
for (std::size_t fromNode = 0; fromNode < data.GetLocations().size();
|
||||
fromNode++) {
|
||||
for (std::size_t toNode = 0; toNode < data.GetLocations().size();
|
||||
toNode++) {
|
||||
if (fromNode != toNode)
|
||||
distances_[fromNode][toNode] =
|
||||
std::abs(data.GetLocations()[toNode][0] -
|
||||
data.GetLocations()[fromNode][0]) +
|
||||
std::abs(data.GetLocations()[toNode][1] -
|
||||
data.GetLocations()[fromNode][1]);
|
||||
/*! @brief Manhattan distance implemented as a callback.
|
||||
* @details It uses an array of positions and
|
||||
* computes the Manhattan distance between the two positions of two different indices.*/
|
||||
class ManhattanDistance {
|
||||
private:
|
||||
std::vector<std::vector<int64>> distances_;
|
||||
public:
|
||||
ManhattanDistance(const DataProblem& data) {
|
||||
// Precompute distance between location to have distance callback in O(1)
|
||||
distances_ = std::vector<std::vector<int64>>(
|
||||
data.GetLocations().size(),
|
||||
std::vector<int64>(
|
||||
data.GetLocations().size(),
|
||||
0LL));
|
||||
for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); fromNode++) {
|
||||
for (std::size_t toNode = 0; toNode < data.GetLocations().size(); toNode++) {
|
||||
if (fromNode != toNode)
|
||||
distances_[fromNode][toNode] =
|
||||
std::abs(data.GetLocations()[toNode][0] - data.GetLocations()[fromNode][0]) +
|
||||
std::abs(data.GetLocations()[toNode][1] - data.GetLocations()[fromNode][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! @brief Returns the manhattan distance between the two nodes.
|
||||
int64 operator()(RoutingIndexManager::NodeIndex FromNode, RoutingIndexManager::NodeIndex ToNode) {
|
||||
return distances_[FromNode.value()][ToNode.value()];
|
||||
}
|
||||
};
|
||||
|
||||
//! @brief Print the solution
|
||||
//! @param[in] data Data of the problem.
|
||||
//! @param[in] manager Index manager used.
|
||||
//! @param[in] routing Routing solver used.
|
||||
//! @param[in] solution Solution found by the solver.
|
||||
void PrintSolution(
|
||||
const DataProblem& data,
|
||||
const RoutingIndexManager& manager,
|
||||
const RoutingModel& routing,
|
||||
const Assignment& solution) {
|
||||
LOG(INFO) << "Objective: " << solution.ObjectiveValue();
|
||||
// Inspect solution.
|
||||
int64 index = routing.Start(0);
|
||||
LOG(INFO) << "Route for Vehicle 0:";
|
||||
int64 distance = 0LL;
|
||||
std::stringstream route;
|
||||
while (routing.IsEnd(index) == false) {
|
||||
route << manager.IndexToNode(index).value() << " -> ";
|
||||
int64 previous_index = index;
|
||||
index = solution.Value(routing.NextVar(index));
|
||||
distance += const_cast<RoutingModel&>(routing).GetArcCostForVehicle(previous_index, index, 0LL);
|
||||
}
|
||||
LOG(INFO) << route.str() << manager.IndexToNode(index).value();
|
||||
LOG(INFO) << "Distance of the route: " << distance << "m";
|
||||
LOG(INFO) << "";
|
||||
LOG(INFO) << "Advanced usage:";
|
||||
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
|
||||
}
|
||||
|
||||
bool IsRepeatable() const override { return true; }
|
||||
void Solve() {
|
||||
// Instantiate the data problem.
|
||||
DataProblem data;
|
||||
|
||||
//! @brief Returns the manhattan distance between the two nodes.
|
||||
int64 Run(RoutingModel::NodeIndex FromNode,
|
||||
RoutingModel::NodeIndex ToNode) override {
|
||||
return distances_[FromNode.value()][ToNode.value()];
|
||||
// Create Routing Index Manager & Routing Model
|
||||
RoutingIndexManager manager(
|
||||
data.GetLocations().size(),
|
||||
data.GetVehicleNumber(),
|
||||
data.GetDepot());
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Define weight of each edge
|
||||
ManhattanDistance distance(data);
|
||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
||||
[&distance, &manager](int64 fromNode, int64 toNode) -> int64 {
|
||||
return distance(manager.IndexToNode(fromNode), manager.IndexToNode(toNode));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters();
|
||||
searchParameters.set_first_solution_strategy(FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
|
||||
const Assignment* solution = routing.SolveWithParameters(searchParameters);
|
||||
PrintSolution(data, manager, routing, *solution);
|
||||
}
|
||||
};
|
||||
|
||||
//! @brief Print the solution
|
||||
//! @param[in] data Data of the problem.
|
||||
//! @param[in] routing Routing solver used.
|
||||
//! @param[in] solution Solution found by the solver.
|
||||
void PrintSolution(const DataProblem& data, const RoutingModel& routing,
|
||||
const Assignment& solution) {
|
||||
LOG(INFO) << "Objective: " << solution.ObjectiveValue();
|
||||
// Inspect solution.
|
||||
int64 index = routing.Start(0);
|
||||
LOG(INFO) << "Route for Vehicle 0:";
|
||||
int64 distance = 0LL;
|
||||
std::stringstream route;
|
||||
while (routing.IsEnd(index) == false) {
|
||||
route << routing.IndexToNode(index).value() << " -> ";
|
||||
int64 previous_index = index;
|
||||
index = solution.Value(routing.NextVar(index));
|
||||
distance += const_cast<RoutingModel&>(routing).GetArcCostForVehicle(
|
||||
previous_index, index, 0LL);
|
||||
}
|
||||
LOG(INFO) << route.str() << routing.IndexToNode(index).value();
|
||||
LOG(INFO) << "Distance of the route: " << distance << "m";
|
||||
LOG(INFO) << "";
|
||||
LOG(INFO) << "Advanced usage:";
|
||||
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
|
||||
}
|
||||
|
||||
void Solve() {
|
||||
// Instantiate the data problem.
|
||||
DataProblem data;
|
||||
|
||||
// Create Routing Model
|
||||
RoutingModel routing(data.GetLocations().size(), data.GetVehicleNumber(),
|
||||
data.GetDepot());
|
||||
|
||||
// Define weight of each edge
|
||||
ManhattanDistance distance(data);
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&distance, &ManhattanDistance::Run));
|
||||
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
RoutingSearchParameters searchParameters =
|
||||
RoutingModel::DefaultSearchParameters();
|
||||
searchParameters.set_first_solution_strategy(
|
||||
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
|
||||
const Assignment* solution = routing.SolveWithParameters(searchParameters);
|
||||
PrintSolution(data, routing, *solution);
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
FLAGS_logtostderr = 1;
|
||||
operations_research::Solve();
|
||||
return EXIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@ void Solve() {
|
||||
const IntVar start_p1 = cp_model.NewIntVar(Domain(500, 800));
|
||||
const IntVar duration_p1 = cp_model.NewIntVar(Domain(1, 360));
|
||||
const IntVar end_p1 = cp_model.NewIntVar(Domain(500, 1000));
|
||||
const IntervalVar p1 = cp_model.NewIntervalVar(start_p1, duration_p1, end_p1);
|
||||
const IntervalVar p1 =
|
||||
cp_model.NewIntervalVar(start_p1, duration_p1, end_p1);
|
||||
|
||||
const IntVar start_p2 = cp_model.NewIntVar(Domain(500, 800));
|
||||
const IntVar duration_p2 = cp_model.NewIntVar(Domain(1, 360));
|
||||
const IntVar end_p2 = cp_model.NewIntVar(Domain(500, 1000));
|
||||
const IntervalVar p2 = cp_model.NewIntervalVar(start_p2, duration_p2, end_p2);
|
||||
const IntervalVar p2 =
|
||||
cp_model.NewIntervalVar(start_p2, duration_p2, end_p2);
|
||||
|
||||
cp_model.AddEquality(LinearExpr::Sum({duration_p1, duration_p2}), 360);
|
||||
cp_model.AddLessOrEqual(end_p1, start_p2);
|
||||
|
||||
@@ -11,149 +11,161 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
#include "ortools/constraint_solver/routing_index_manager.h"
|
||||
#include "ortools/constraint_solver/routing_parameters.h"
|
||||
|
||||
namespace operations_research {
|
||||
class DataProblem {
|
||||
private:
|
||||
std::vector<std::vector<int>> locations_;
|
||||
class DataProblem {
|
||||
private:
|
||||
std::vector<std::vector<int>> locations_;
|
||||
|
||||
public:
|
||||
DataProblem() {
|
||||
locations_ = {{4, 4}, {2, 0}, {8, 0}, {0, 1}, {1, 1}, {5, 2},
|
||||
{7, 2}, {3, 3}, {6, 3}, {5, 5}, {8, 5}, {1, 6},
|
||||
{2, 6}, {3, 7}, {6, 7}, {0, 8}, {7, 8}};
|
||||
public:
|
||||
DataProblem() {
|
||||
locations_ = {
|
||||
{4, 4},
|
||||
{2, 0}, {8, 0},
|
||||
{0, 1}, {1, 1},
|
||||
{5, 2}, {7, 2},
|
||||
{3, 3}, {6, 3},
|
||||
{5, 5}, {8, 5},
|
||||
{1, 6}, {2, 6},
|
||||
{3, 7}, {6, 7},
|
||||
{0, 8}, {7, 8}
|
||||
};
|
||||
|
||||
// Compute locations in meters using the block dimension defined as follow
|
||||
// Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
// here we use: 114m x 80m city block
|
||||
// src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
|
||||
std::array<int, 2> cityBlock = {228 / 2, 80};
|
||||
for (auto& i : locations_) {
|
||||
i[0] = i[0] * cityBlock[0];
|
||||
i[1] = i[1] * cityBlock[1];
|
||||
}
|
||||
}
|
||||
// Compute locations in meters using the block dimension defined as follow
|
||||
// Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
// here we use: 114m x 80m city block
|
||||
// src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
|
||||
std::array<int, 2> cityBlock = {228/2, 80};
|
||||
for (auto &i: locations_) {
|
||||
i[0] = i[0] * cityBlock[0];
|
||||
i[1] = i[1] * cityBlock[1];
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t GetVehicleNumber() const { return 4; }
|
||||
const std::vector<std::vector<int>>& GetLocations() const {
|
||||
return locations_;
|
||||
}
|
||||
RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode; }
|
||||
};
|
||||
std::size_t GetVehicleNumber() const { return 4;}
|
||||
const std::vector<std::vector<int>>& GetLocations() const { return locations_;}
|
||||
RoutingIndexManager::NodeIndex GetDepot() const { return RoutingIndexManager::NodeIndex(0);}
|
||||
};
|
||||
|
||||
/*! @brief Manhattan distance implemented as a callback.
|
||||
* @details It uses an array of positions and
|
||||
* computes the Manhattan distance between the two positions of two different
|
||||
* indices.*/
|
||||
class ManhattanDistance : public RoutingModel::NodeEvaluator2 {
|
||||
private:
|
||||
std::vector<std::vector<int64>> distances_;
|
||||
|
||||
public:
|
||||
ManhattanDistance(const DataProblem& data) {
|
||||
// Precompute distance between location to have distance callback in O(1)
|
||||
distances_ = std::vector<std::vector<int64>>(
|
||||
data.GetLocations().size(),
|
||||
std::vector<int64>(data.GetLocations().size(), 0LL));
|
||||
for (std::size_t fromNode = 0; fromNode < data.GetLocations().size();
|
||||
fromNode++) {
|
||||
for (std::size_t toNode = 0; toNode < data.GetLocations().size();
|
||||
toNode++) {
|
||||
if (fromNode != toNode)
|
||||
distances_[fromNode][toNode] =
|
||||
std::abs(data.GetLocations()[toNode][0] -
|
||||
data.GetLocations()[fromNode][0]) +
|
||||
std::abs(data.GetLocations()[toNode][1] -
|
||||
data.GetLocations()[fromNode][1]);
|
||||
/*! @brief Manhattan distance implemented as a callback.
|
||||
* @details It uses an array of positions and
|
||||
* computes the Manhattan distance between the two positions of two different indices.*/
|
||||
class ManhattanDistance {
|
||||
private:
|
||||
std::vector<std::vector<int64>> distances_;
|
||||
public:
|
||||
ManhattanDistance(const DataProblem& data) {
|
||||
// Precompute distance between location to have distance callback in O(1)
|
||||
distances_ = std::vector<std::vector<int64>>(
|
||||
data.GetLocations().size(),
|
||||
std::vector<int64>(
|
||||
data.GetLocations().size(),
|
||||
0LL));
|
||||
for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); fromNode++) {
|
||||
for (std::size_t toNode = 0; toNode < data.GetLocations().size(); toNode++) {
|
||||
if (fromNode != toNode)
|
||||
distances_[fromNode][toNode] =
|
||||
std::abs(data.GetLocations()[toNode][0] - data.GetLocations()[fromNode][0]) +
|
||||
std::abs(data.GetLocations()[toNode][1] - data.GetLocations()[fromNode][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsRepeatable() const override { return true; }
|
||||
|
||||
//! @brief Returns the manhattan distance between the two nodes.
|
||||
int64 Run(RoutingModel::NodeIndex FromNode,
|
||||
RoutingModel::NodeIndex ToNode) override {
|
||||
return distances_[FromNode.value()][ToNode.value()];
|
||||
}
|
||||
};
|
||||
|
||||
//! @brief Add distance Dimension.
|
||||
//! @param[in] data Data of the problem.
|
||||
//! @param[in, out] routing Routing solver used.
|
||||
static void AddDistanceDimension(const DataProblem& data,
|
||||
RoutingModel* routing) {
|
||||
std::string distance("Distance");
|
||||
routing->AddDimension(new ManhattanDistance(data),
|
||||
0, // null slack
|
||||
3000, // maximum distance per vehicle
|
||||
true, // start cumul to zero
|
||||
distance);
|
||||
RoutingDimension* distanceDimension = routing->GetMutableDimension(distance);
|
||||
// Try to minimize the max distance among vehicles.
|
||||
// /!\ It doesn't mean the standard deviation is minimized
|
||||
distanceDimension->SetGlobalSpanCostCoefficient(100);
|
||||
}
|
||||
|
||||
//! @brief Print the solution
|
||||
//! @param[in] data Data of the problem.
|
||||
//! @param[in] routing Routing solver used.
|
||||
//! @param[in] solution Solution found by the solver.
|
||||
void PrintSolution(const DataProblem& data, const RoutingModel& routing,
|
||||
const Assignment& solution) {
|
||||
LOG(INFO) << "Objective: " << solution.ObjectiveValue();
|
||||
// Inspect solution.
|
||||
for (int i = 0; i < data.GetVehicleNumber(); ++i) {
|
||||
int64 index = routing.Start(i);
|
||||
LOG(INFO) << "Route for Vehicle " << i << ":";
|
||||
int64 distance = 0LL;
|
||||
std::stringstream route;
|
||||
while (routing.IsEnd(index) == false) {
|
||||
route << routing.IndexToNode(index).value() << " -> ";
|
||||
int64 previous_index = index;
|
||||
index = solution.Value(routing.NextVar(index));
|
||||
distance += const_cast<RoutingModel&>(routing).GetArcCostForVehicle(
|
||||
previous_index, index, i);
|
||||
//! @brief Returns the manhattan distance between the two nodes.
|
||||
int64 operator()(RoutingIndexManager::NodeIndex FromNode, RoutingIndexManager::NodeIndex ToNode) {
|
||||
return distances_[FromNode.value()][ToNode.value()];
|
||||
}
|
||||
LOG(INFO) << route.str() << routing.IndexToNode(index).value();
|
||||
LOG(INFO) << "Distance of the route: " << distance << "m";
|
||||
};
|
||||
|
||||
//! @brief Add distance Dimension.
|
||||
//! @param[in] data Data of the problem.
|
||||
//! @param[in] callback transit cost callback.
|
||||
//! @param[in, out] routing Routing solver used.
|
||||
static void AddDistanceDimension(const DataProblem& data, const int callback, RoutingModel* routing) {
|
||||
std::string distance("Distance");
|
||||
routing->AddDimension(
|
||||
callback,
|
||||
0, // null slack
|
||||
3000, // maximum distance per vehicle
|
||||
true, // start cumul to zero
|
||||
distance);
|
||||
RoutingDimension* distanceDimension = routing->GetMutableDimension(distance);
|
||||
// Try to minimize the max distance among vehicles.
|
||||
// /!\ It doesn't mean the standard deviation is minimized
|
||||
distanceDimension->SetGlobalSpanCostCoefficient(100);
|
||||
}
|
||||
LOG(INFO) << "";
|
||||
LOG(INFO) << "Advanced usage:";
|
||||
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
|
||||
}
|
||||
|
||||
void Solve() {
|
||||
// Instantiate the data problem.
|
||||
DataProblem data;
|
||||
//! @brief Print the solution
|
||||
//! @param[in] data Data of the problem.
|
||||
//! @param[in] manager Index manager used.
|
||||
//! @param[in] routing Routing solver used.
|
||||
//! @param[in] solution Solution found by the solver.
|
||||
void PrintSolution(
|
||||
const DataProblem& data,
|
||||
const RoutingIndexManager& manager,
|
||||
const RoutingModel& routing,
|
||||
const Assignment& solution) {
|
||||
LOG(INFO) << "Objective: " << solution.ObjectiveValue();
|
||||
// Inspect solution.
|
||||
for (int i=0; i < data.GetVehicleNumber(); ++i) {
|
||||
int64 index = routing.Start(i);
|
||||
LOG(INFO) << "Route for Vehicle " << i << ":";
|
||||
int64 distance = 0LL;
|
||||
std::stringstream route;
|
||||
while (routing.IsEnd(index) == false) {
|
||||
route << manager.IndexToNode(index).value() << " -> ";
|
||||
int64 previous_index = index;
|
||||
index = solution.Value(routing.NextVar(index));
|
||||
distance += const_cast<RoutingModel&>(routing).GetArcCostForVehicle(previous_index, index, i);
|
||||
}
|
||||
LOG(INFO) << route.str() << manager.IndexToNode(index).value();
|
||||
LOG(INFO) << "Distance of the route: " << distance << "m";
|
||||
}
|
||||
LOG(INFO) << "";
|
||||
LOG(INFO) << "Advanced usage:";
|
||||
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
|
||||
}
|
||||
|
||||
// Create Routing Model
|
||||
RoutingModel routing(data.GetLocations().size(), data.GetVehicleNumber(),
|
||||
data.GetDepot());
|
||||
void Solve() {
|
||||
// Instantiate the data problem.
|
||||
DataProblem data;
|
||||
|
||||
// Define weight of each edge
|
||||
ManhattanDistance distance(data);
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&distance, &ManhattanDistance::Run));
|
||||
AddDistanceDimension(data, &routing);
|
||||
// Create Routing Index Manager & Routing Model
|
||||
RoutingIndexManager manager(
|
||||
data.GetLocations().size(),
|
||||
data.GetVehicleNumber(),
|
||||
data.GetDepot());
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
auto searchParameters = RoutingModel::DefaultSearchParameters();
|
||||
searchParameters.set_first_solution_strategy(
|
||||
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
// Define weight of each edge
|
||||
ManhattanDistance distance(data);
|
||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
||||
[&distance, &manager](int64 fromNode, int64 toNode) -> int64 {
|
||||
return distance(manager.IndexToNode(fromNode), manager.IndexToNode(toNode));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
AddDistanceDimension(data, vehicle_cost, &routing);
|
||||
|
||||
const Assignment* solution = routing.SolveWithParameters(searchParameters);
|
||||
PrintSolution(data, routing, *solution);
|
||||
}
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters();
|
||||
searchParameters.set_first_solution_strategy(
|
||||
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
|
||||
const Assignment* solution = routing.SolveWithParameters(searchParameters);
|
||||
PrintSolution(data, manager, routing, *solution);
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
FLAGS_logtostderr = 1;
|
||||
operations_research::Solve();
|
||||
return EXIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,15 +15,14 @@
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "google/protobuf/text_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/filelineiter.h"
|
||||
#include "ortools/base/join.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/numbers.h"
|
||||
#include "ortools/base/split.h"
|
||||
#include "ortools/base/strtoint.h"
|
||||
#include "ortools/base/strutil.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
#include "ortools/sat/model.h"
|
||||
@@ -246,7 +245,7 @@ void ParseAndSolve() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
base::SetFlag(&FLAGS_logtostderr, true);
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_input.empty()) {
|
||||
LOG(FATAL) << "Please supply a data file with --input=";
|
||||
|
||||
Reference in New Issue
Block a user