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:
Corentin Le Molgat
2018-10-31 16:18:18 +01:00
parent 745906cb7c
commit b027e57e95
490 changed files with 92044 additions and 24779 deletions

View File

@@ -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

View File

@@ -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, &parameters));
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));

View File

@@ -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, &parameters));
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));

View File

@@ -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,

View File

@@ -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, &parameters));
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 {

View File

@@ -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, &parameters));
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));

View File

@@ -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, &parameters));
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));

View File

@@ -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, &parameters));
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));

View File

@@ -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);

View File

@@ -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(

View File

@@ -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_

View File

@@ -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);

View File

@@ -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=";

View File

@@ -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

View File

@@ -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, &params, 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) {

View File

@@ -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

View File

@@ -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"

View File

@@ -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;

View File

@@ -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()));
}
}

View File

@@ -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, &parameters));
// 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.";

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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=";

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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=";