backport example/ from main
This commit is contained in:
@@ -611,79 +611,6 @@ cc_binary(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "cvrptw_lib",
|
|
||||||
hdrs = ["cvrptw_lib.h"],
|
|
||||||
deps = [
|
|
||||||
"//ortools/base",
|
|
||||||
"//ortools/constraint_solver:routing",
|
|
||||||
"//ortools/util:random_engine",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "cvrptw",
|
|
||||||
srcs = ["cvrptw.cc"],
|
|
||||||
deps = [
|
|
||||||
":cvrptw_lib",
|
|
||||||
"//ortools/base",
|
|
||||||
"//ortools/constraint_solver:routing",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "cvrp_disjoint_tw",
|
|
||||||
srcs = ["cvrp_disjoint_tw.cc"],
|
|
||||||
deps = [
|
|
||||||
":cvrptw_lib",
|
|
||||||
"//ortools/base",
|
|
||||||
"//ortools/constraint_solver:routing",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "cvrptw_with_breaks",
|
|
||||||
srcs = ["cvrptw_with_breaks.cc"],
|
|
||||||
deps = [
|
|
||||||
":cvrptw_lib",
|
|
||||||
"//ortools/base",
|
|
||||||
"//ortools/constraint_solver:routing",
|
|
||||||
"//ortools/constraint_solver:routing_enums_cc_proto",
|
|
||||||
"@com_google_absl//absl/strings",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "cvrptw_with_resources",
|
|
||||||
srcs = ["cvrptw_with_resources.cc"],
|
|
||||||
deps = [
|
|
||||||
":cvrptw_lib",
|
|
||||||
"//ortools/base",
|
|
||||||
"//ortools/constraint_solver:routing",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "cvrptw_with_stop_times_and_resources",
|
|
||||||
srcs = ["cvrptw_with_stop_times_and_resources.cc"],
|
|
||||||
deps = [
|
|
||||||
":cvrptw_lib",
|
|
||||||
"//ortools/base",
|
|
||||||
"//ortools/constraint_solver:routing",
|
|
||||||
"@com_google_absl//absl/strings",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "cvrptw_with_refueling",
|
|
||||||
srcs = ["cvrptw_with_refueling.cc"],
|
|
||||||
deps = [
|
|
||||||
":cvrptw_lib",
|
|
||||||
"//ortools/base",
|
|
||||||
"//ortools/constraint_solver:routing",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
name = "pdptw",
|
name = "pdptw",
|
||||||
srcs = ["pdptw.cc"],
|
srcs = ["pdptw.cc"],
|
||||||
@@ -692,6 +619,7 @@ cc_binary(
|
|||||||
"//ortools/base:file",
|
"//ortools/base:file",
|
||||||
"//ortools/base:mathutil",
|
"//ortools/base:mathutil",
|
||||||
"//ortools/constraint_solver:routing",
|
"//ortools/constraint_solver:routing",
|
||||||
|
"//ortools/routing/parsers:lilim_parser",
|
||||||
"@com_google_absl//absl/flags:flag",
|
"@com_google_absl//absl/flags:flag",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
"@com_google_absl//absl/strings:str_format",
|
"@com_google_absl//absl/strings:str_format",
|
||||||
@@ -753,6 +681,7 @@ cc_binary(
|
|||||||
"//ortools/base",
|
"//ortools/base",
|
||||||
"//ortools/linear_solver",
|
"//ortools/linear_solver",
|
||||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||||
|
"//ortools/linear_solver:solve_mp_model",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1075,12 +1004,18 @@ cc_binary(
|
|||||||
deps = [
|
deps = [
|
||||||
"//ortools/base",
|
"//ortools/base",
|
||||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||||
|
"//ortools/pdlp:iteration_stats",
|
||||||
"//ortools/pdlp:primal_dual_hybrid_gradient",
|
"//ortools/pdlp:primal_dual_hybrid_gradient",
|
||||||
|
"//ortools/pdlp:quadratic_program",
|
||||||
"//ortools/pdlp:quadratic_program_io",
|
"//ortools/pdlp:quadratic_program_io",
|
||||||
"//ortools/pdlp:solve_log_cc_proto",
|
"//ortools/pdlp:solve_log_cc_proto",
|
||||||
"//ortools/pdlp:solvers_cc_proto",
|
"//ortools/pdlp:solvers_cc_proto",
|
||||||
"//ortools/port:proto_utils",
|
"//ortools/port:proto_utils",
|
||||||
|
"//ortools/util:file_util",
|
||||||
"//ortools/util:sigint",
|
"//ortools/util:sigint",
|
||||||
"@com_google_absl//absl/time",
|
"@com_google_absl//absl/flags:flag",
|
||||||
|
"@com_google_absl//absl/log:check",
|
||||||
|
"@com_google_absl//absl/log:flags",
|
||||||
|
"@com_google_absl//absl/strings",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ file(GLOB CXX_SRCS "*.cc")
|
|||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/binpacking_2d_sat.cc")
|
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/binpacking_2d_sat.cc")
|
||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling_run.cc") # missing proto
|
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling_run.cc") # missing proto
|
||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling.cc") # missing proto
|
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling.cc") # missing proto
|
||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/cvrptw_with_breaks.cc") # too long
|
|
||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/cvrptw_with_refueling.cc") # too long
|
|
||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dimacs_assignment.cc") # crash
|
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dimacs_assignment.cc") # crash
|
||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dobble_ls.cc") # Too long
|
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dobble_ls.cc") # Too long
|
||||||
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/frequency_assignment_problem.cc") # crash
|
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/frequency_assignment_problem.cc") # crash
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "absl/flags/flag.h"
|
#include "absl/flags/flag.h"
|
||||||
#include "absl/log/check.h"
|
#include "absl/log/check.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "absl/types/span.h"
|
||||||
#include "google/protobuf/text_format.h"
|
#include "google/protobuf/text_format.h"
|
||||||
#include "ortools/base/init_google.h"
|
#include "ortools/base/init_google.h"
|
||||||
#include "ortools/base/logging.h"
|
#include "ortools/base/logging.h"
|
||||||
@@ -171,7 +172,7 @@ absl::btree_set<int> FindFixedItems(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Solves a subset sum problem to find the maximum reachable max size.
|
// Solves a subset sum problem to find the maximum reachable max size.
|
||||||
int64_t MaxSubsetSumSize(const std::vector<int64_t>& sizes, int64_t max_size) {
|
int64_t MaxSubsetSumSize(absl::Span<const int64_t> sizes, int64_t max_size) {
|
||||||
CpModelBuilder builder;
|
CpModelBuilder builder;
|
||||||
LinearExpr weighed_sum;
|
LinearExpr weighed_sum;
|
||||||
for (const int size : sizes) {
|
for (const int size : sizes) {
|
||||||
@@ -280,7 +281,7 @@ void LoadAndSolve(const std::string& file_name, int instance) {
|
|||||||
const absl::btree_set<int> fixed_items = FindFixedItems(problem);
|
const absl::btree_set<int> fixed_items = FindFixedItems(problem);
|
||||||
|
|
||||||
// Fix the fixed_items to the first fixed_items.size() bins.
|
// Fix the fixed_items to the first fixed_items.size() bins.
|
||||||
CHECK_LT(fixed_items.size(), max_bins)
|
CHECK_LE(fixed_items.size(), max_bins)
|
||||||
<< "Infeasible problem, increase max_bins";
|
<< "Infeasible problem, increase max_bins";
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (const int item : fixed_items) {
|
for (const int item : fixed_items) {
|
||||||
@@ -437,9 +438,10 @@ void LoadAndSolve(const std::string& file_name, int instance) {
|
|||||||
|
|
||||||
// Objective definition.
|
// Objective definition.
|
||||||
cp_model.Minimize(obj);
|
cp_model.Minimize(obj);
|
||||||
for (int b = trivial_lb; b + 1 < max_bins; ++b) {
|
CHECK_GT(trivial_lb, 0);
|
||||||
|
for (int b = trivial_lb; b < max_bins; ++b) {
|
||||||
cp_model.AddGreaterOrEqual(obj, b + 1).OnlyEnforceIf(bin_is_used[b]);
|
cp_model.AddGreaterOrEqual(obj, b + 1).OnlyEnforceIf(bin_is_used[b]);
|
||||||
cp_model.AddImplication(bin_is_used[b + 1], bin_is_used[b]);
|
cp_model.AddImplication(bin_is_used[b], bin_is_used[b - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absl::GetFlag(FLAGS_symmetry_breaking)) {
|
if (absl::GetFlag(FLAGS_symmetry_breaking)) {
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ void RunConstraintProgrammingExample() {
|
|||||||
|
|
||||||
solver.NewSearch(db);
|
solver.NewSearch(db);
|
||||||
while (solver.NextSolution()) {
|
while (solver.NextSolution()) {
|
||||||
LOG(INFO) << "Solution"
|
LOG(INFO) << "Solution" << ": x = " << x->Value() << "; y = " << y->Value()
|
||||||
<< ": x = " << x->Value() << "; y = " << y->Value()
|
|
||||||
<< "; z = " << z->Value();
|
<< "; z = " << z->Value();
|
||||||
}
|
}
|
||||||
solver.EndSearch();
|
solver.EndSearch();
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ void CheckConstraintViolators(absl::Span<const int64_t> vars,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that all pairwise differences are unique
|
// Check that all pairwise differences are unique
|
||||||
bool CheckCostas(const std::vector<int64_t>& vars) {
|
bool CheckCostas(absl::Span<const int64_t> vars) {
|
||||||
std::vector<int> violators;
|
std::vector<int> violators;
|
||||||
|
|
||||||
CheckConstraintViolators(vars, &violators);
|
CheckConstraintViolators(vars, &violators);
|
||||||
|
|||||||
@@ -1,195 +0,0 @@
|
|||||||
// Copyright 2010-2024 Google LLC
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//
|
|
||||||
// Capacitated Vehicle Routing Problem with Disjoint Time Windows (and optional
|
|
||||||
// orders).
|
|
||||||
// A description of the problem can be found here:
|
|
||||||
// http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
|
||||||
// The variant which is tackled by this model includes a capacity dimension,
|
|
||||||
// disjoint time windows and optional orders, with a penalty cost if orders are
|
|
||||||
// not performed. For the sake of simplicity, orders are randomly located and
|
|
||||||
// distances are computed using the Manhattan distance. Distances are assumed
|
|
||||||
// to be in meters and times in seconds.
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "absl/random/random.h"
|
|
||||||
#include "examples/cpp/cvrptw_lib.h"
|
|
||||||
#include "google/protobuf/text_format.h"
|
|
||||||
#include "ortools/base/commandlineflags.h"
|
|
||||||
#include "ortools/base/init_google.h"
|
|
||||||
#include "ortools/base/types.h"
|
|
||||||
#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"
|
|
||||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
ABSL_FLAG(int, vrp_orders, 100, "Number of nodes in the problem.");
|
|
||||||
ABSL_FLAG(int, vrp_vehicles, 20, "Number of vehicles in the problem.");
|
|
||||||
ABSL_FLAG(int, vrp_windows, 5, "Number of disjoint windows per node.");
|
|
||||||
ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
|
|
||||||
"Use deterministic random seeds.");
|
|
||||||
ABSL_FLAG(bool, vrp_use_same_vehicle_costs, false,
|
|
||||||
"Use same vehicle costs in the routing model");
|
|
||||||
ABSL_FLAG(std::string, routing_search_parameters, "",
|
|
||||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
|
||||||
"override the DefaultRoutingSearchParameters()");
|
|
||||||
|
|
||||||
const char* kTime = "Time";
|
|
||||||
const char* kCapacity = "Capacity";
|
|
||||||
const int64_t kMaxNodesPerGroup = 10;
|
|
||||||
const int64_t kSameVehicleCost = 1000;
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
InitGoogle(argv[0], &argc, &argv, true);
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
|
||||||
<< "Specify an instance size greater than 0.";
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
|
||||||
<< "Specify a non-null vehicle fleet size.";
|
|
||||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
|
||||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
|
||||||
// ends of the routes are at node 0.
|
|
||||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
|
||||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
|
||||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
|
||||||
RoutingModel routing(manager);
|
|
||||||
|
|
||||||
// Setting up locations.
|
|
||||||
const int64_t kXMax = 100000;
|
|
||||||
const int64_t kYMax = 100000;
|
|
||||||
const int64_t kSpeed = 10;
|
|
||||||
LocationContainer locations(
|
|
||||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
|
||||||
++location) {
|
|
||||||
locations.AddRandomLocation(kXMax, kYMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the cost function.
|
|
||||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
|
||||||
[&locations, &manager](int64_t i, int64_t j) {
|
|
||||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
|
||||||
manager.IndexToNode(j));
|
|
||||||
});
|
|
||||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
|
||||||
|
|
||||||
// Adding capacity dimension constraints.
|
|
||||||
const int64_t kVehicleCapacity = 40;
|
|
||||||
const int64_t kNullCapacitySlack = 0;
|
|
||||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
demand.Initialize();
|
|
||||||
routing.AddDimension(routing.RegisterTransitCallback(
|
|
||||||
[&demand, &manager](int64_t i, int64_t 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_t kTimePerDemandUnit = 300;
|
|
||||||
const int64_t kHorizon = 24 * 3600;
|
|
||||||
ServiceTimePlusTransition time(
|
|
||||||
kTimePerDemandUnit,
|
|
||||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return demand.Demand(i, j);
|
|
||||||
},
|
|
||||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return locations.ManhattanTime(i, j);
|
|
||||||
});
|
|
||||||
routing.AddDimension(
|
|
||||||
routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t 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 disjoint time windows.
|
|
||||||
Solver* solver = routing.solver();
|
|
||||||
std::mt19937 randomizer(
|
|
||||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
|
||||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
|
||||||
std::vector<int64_t> forbid_points(2 * absl::GetFlag(FLAGS_vrp_windows), 0);
|
|
||||||
for (int i = 0; i < forbid_points.size(); ++i) {
|
|
||||||
forbid_points[i] = absl::Uniform<int32_t>(randomizer, 0, kHorizon);
|
|
||||||
}
|
|
||||||
std::sort(forbid_points.begin(), forbid_points.end());
|
|
||||||
std::vector<int64_t> forbid_starts(1, 0);
|
|
||||||
std::vector<int64_t> forbid_ends;
|
|
||||||
for (int i = 0; i < forbid_points.size(); i += 2) {
|
|
||||||
forbid_ends.push_back(forbid_points[i]);
|
|
||||||
forbid_starts.push_back(forbid_points[i + 1]);
|
|
||||||
}
|
|
||||||
forbid_ends.push_back(kHorizon);
|
|
||||||
solver->AddConstraint(solver->MakeNotMemberCt(
|
|
||||||
time_dimension.CumulVar(order), forbid_starts, forbid_ends));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding penalty costs to allow skipping orders.
|
|
||||||
const int64_t kPenalty = 10000000;
|
|
||||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
|
||||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
|
||||||
order < manager.num_nodes(); ++order) {
|
|
||||||
std::vector<int64_t> orders(1, manager.NodeToIndex(order));
|
|
||||||
routing.AddDisjunction(orders, kPenalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding same vehicle constraint costs for consecutive nodes.
|
|
||||||
if (absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs)) {
|
|
||||||
std::vector<int64_t> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!group.empty()) {
|
|
||||||
routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve, returns a solution if any (owned by RoutingModel).
|
|
||||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
|
||||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
|
||||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
|
||||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
|
||||||
if (solution != nullptr) {
|
|
||||||
DisplayPlan(manager, routing, *solution,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs),
|
|
||||||
kMaxNodesPerGroup, kSameVehicleCost,
|
|
||||||
routing.GetDimensionOrDie(kCapacity),
|
|
||||||
routing.GetDimensionOrDie(kTime));
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << "No solution found.";
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
// Copyright 2010-2024 Google LLC
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//
|
|
||||||
// Capacitated Vehicle Routing Problem with Time Windows (and optional orders).
|
|
||||||
// A description of the problem can be found here:
|
|
||||||
// http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
|
||||||
// The variant which is tackled by this model includes a capacity dimension,
|
|
||||||
// time windows and optional orders, with a penalty cost if orders are not
|
|
||||||
// performed. For the sake of simplicity, orders are randomly located and
|
|
||||||
// distances are computed using the Manhattan distance. Distances are assumed
|
|
||||||
// to be in meters and times in seconds.
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "absl/random/random.h"
|
|
||||||
#include "examples/cpp/cvrptw_lib.h"
|
|
||||||
#include "google/protobuf/text_format.h"
|
|
||||||
#include "ortools/base/commandlineflags.h"
|
|
||||||
#include "ortools/base/init_google.h"
|
|
||||||
#include "ortools/base/types.h"
|
|
||||||
#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"
|
|
||||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
ABSL_FLAG(int, vrp_orders, 100, "Number of nodes in the problem");
|
|
||||||
ABSL_FLAG(int, vrp_vehicles, 20, "Number of vehicles in the problem");
|
|
||||||
ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
|
|
||||||
"Use deterministic random seeds");
|
|
||||||
ABSL_FLAG(bool, vrp_use_same_vehicle_costs, false,
|
|
||||||
"Use same vehicle costs in the routing model");
|
|
||||||
ABSL_FLAG(std::string, routing_search_parameters, "",
|
|
||||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
|
||||||
"override the DefaultRoutingSearchParameters()");
|
|
||||||
|
|
||||||
const char* kTime = "Time";
|
|
||||||
const char* kCapacity = "Capacity";
|
|
||||||
const int64_t kMaxNodesPerGroup = 10;
|
|
||||||
const int64_t kSameVehicleCost = 1000;
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
InitGoogle(argv[0], &argc, &argv, true);
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
|
||||||
<< "Specify an instance size greater than 0.";
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
|
||||||
<< "Specify a non-null vehicle fleet size.";
|
|
||||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
|
||||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
|
||||||
// ends of the routes are at node 0.
|
|
||||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
|
||||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
|
||||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
|
||||||
RoutingModel routing(manager);
|
|
||||||
|
|
||||||
// Setting up locations.
|
|
||||||
const int64_t kXMax = 100000;
|
|
||||||
const int64_t kYMax = 100000;
|
|
||||||
const int64_t kSpeed = 10;
|
|
||||||
LocationContainer locations(
|
|
||||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
|
||||||
++location) {
|
|
||||||
locations.AddRandomLocation(kXMax, kYMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the cost function.
|
|
||||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
|
||||||
[&locations, &manager](int64_t i, int64_t j) {
|
|
||||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
|
||||||
manager.IndexToNode(j));
|
|
||||||
});
|
|
||||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
|
||||||
|
|
||||||
// Adding capacity dimension constraints.
|
|
||||||
const int64_t kVehicleCapacity = 40;
|
|
||||||
const int64_t kNullCapacitySlack = 0;
|
|
||||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
demand.Initialize();
|
|
||||||
routing.AddDimension(routing.RegisterTransitCallback(
|
|
||||||
[&demand, &manager](int64_t i, int64_t 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_t kTimePerDemandUnit = 300;
|
|
||||||
const int64_t kHorizon = 24 * 3600;
|
|
||||||
ServiceTimePlusTransition time(
|
|
||||||
kTimePerDemandUnit,
|
|
||||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return demand.Demand(i, j);
|
|
||||||
},
|
|
||||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return locations.ManhattanTime(i, j);
|
|
||||||
});
|
|
||||||
routing.AddDimension(
|
|
||||||
routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) {
|
|
||||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
|
||||||
}),
|
|
||||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
|
||||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
|
||||||
|
|
||||||
// Adding time windows.
|
|
||||||
std::mt19937 randomizer(
|
|
||||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
|
||||||
const int64_t kTWDuration = 5 * 3600;
|
|
||||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
|
||||||
const int64_t start =
|
|
||||||
absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
|
|
||||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding penalty costs to allow skipping orders.
|
|
||||||
const int64_t kPenalty = 10000000;
|
|
||||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
|
||||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
|
||||||
order < manager.num_nodes(); ++order) {
|
|
||||||
std::vector<int64_t> orders(1, manager.NodeToIndex(order));
|
|
||||||
routing.AddDisjunction(orders, kPenalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding same vehicle constraint costs for consecutive nodes.
|
|
||||||
if (absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs)) {
|
|
||||||
std::vector<int64_t> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!group.empty()) {
|
|
||||||
routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve, returns a solution if any (owned by RoutingModel).
|
|
||||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
|
||||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
|
||||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
|
||||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
|
||||||
if (solution != nullptr) {
|
|
||||||
DisplayPlan(manager, routing, *solution,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs),
|
|
||||||
kMaxNodesPerGroup, kSameVehicleCost,
|
|
||||||
routing.GetDimensionOrDie(kCapacity),
|
|
||||||
routing.GetDimensionOrDie(kTime));
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << "No solution found.";
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
// Copyright 2010-2024 Google LLC
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// This header provides functions to help creating random instaces of the
|
|
||||||
// vehicle routing problem; random capacities and random time windows.
|
|
||||||
#ifndef OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
|
||||||
#define OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "absl/strings/str_format.h"
|
|
||||||
#include "ortools/base/logging.h"
|
|
||||||
#include "ortools/constraint_solver/routing.h"
|
|
||||||
#include "ortools/util/random_engine.h"
|
|
||||||
|
|
||||||
namespace operations_research {
|
|
||||||
|
|
||||||
typedef std::function<int64_t(RoutingNodeIndex, RoutingNodeIndex)>
|
|
||||||
RoutingNodeEvaluator2;
|
|
||||||
|
|
||||||
// Random seed generator.
|
|
||||||
int32_t GetSeed(bool deterministic);
|
|
||||||
|
|
||||||
// Location container, contains positions of orders and can be used to obtain
|
|
||||||
// Manhattan distances/times between locations.
|
|
||||||
class LocationContainer {
|
|
||||||
public:
|
|
||||||
LocationContainer(int64_t speed, bool use_deterministic_seed);
|
|
||||||
void AddLocation(int64_t x, int64_t y) {
|
|
||||||
locations_.push_back(Location(x, y));
|
|
||||||
}
|
|
||||||
void AddRandomLocation(int64_t x_max, int64_t y_max);
|
|
||||||
void AddRandomLocation(int64_t x_max, int64_t y_max, int duplicates);
|
|
||||||
int64_t ManhattanDistance(RoutingIndexManager::NodeIndex from,
|
|
||||||
RoutingIndexManager::NodeIndex to) const;
|
|
||||||
int64_t NegManhattanDistance(RoutingIndexManager::NodeIndex from,
|
|
||||||
RoutingIndexManager::NodeIndex to) const;
|
|
||||||
int64_t ManhattanTime(RoutingIndexManager::NodeIndex from,
|
|
||||||
RoutingIndexManager::NodeIndex to) const;
|
|
||||||
|
|
||||||
bool SameLocation(RoutingIndexManager::NodeIndex node1,
|
|
||||||
RoutingIndexManager::NodeIndex node2) const;
|
|
||||||
int64_t SameLocationFromIndex(int64_t node1, int64_t node2) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Location {
|
|
||||||
public:
|
|
||||||
Location();
|
|
||||||
Location(int64_t x, int64_t y);
|
|
||||||
int64_t DistanceTo(const Location& location) const;
|
|
||||||
bool IsAtSameLocation(const Location& location) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static int64_t Abs(int64_t value);
|
|
||||||
|
|
||||||
int64_t x_;
|
|
||||||
int64_t y_;
|
|
||||||
};
|
|
||||||
|
|
||||||
random_engine_t randomizer_;
|
|
||||||
const int64_t speed_;
|
|
||||||
absl::StrongVector<RoutingIndexManager::NodeIndex, Location> locations_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Random demand.
|
|
||||||
class RandomDemand {
|
|
||||||
public:
|
|
||||||
RandomDemand(int size, RoutingIndexManager::NodeIndex depot,
|
|
||||||
bool use_deterministic_seed);
|
|
||||||
void Initialize();
|
|
||||||
int64_t Demand(RoutingIndexManager::NodeIndex from,
|
|
||||||
RoutingIndexManager::NodeIndex to) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<int64_t[]> demand_;
|
|
||||||
const int size_;
|
|
||||||
const RoutingIndexManager::NodeIndex depot_;
|
|
||||||
const bool use_deterministic_seed_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Service time (proportional to demand) + transition time callback.
|
|
||||||
class ServiceTimePlusTransition {
|
|
||||||
public:
|
|
||||||
ServiceTimePlusTransition(
|
|
||||||
int64_t time_per_demand_unit,
|
|
||||||
operations_research::RoutingNodeEvaluator2 demand,
|
|
||||||
operations_research::RoutingNodeEvaluator2 transition_time);
|
|
||||||
int64_t Compute(RoutingIndexManager::NodeIndex from,
|
|
||||||
RoutingIndexManager::NodeIndex to) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const int64_t time_per_demand_unit_;
|
|
||||||
operations_research::RoutingNodeEvaluator2 demand_;
|
|
||||||
operations_research::RoutingNodeEvaluator2 transition_time_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stop service time + transition time callback.
|
|
||||||
class StopServiceTimePlusTransition {
|
|
||||||
public:
|
|
||||||
StopServiceTimePlusTransition(
|
|
||||||
int64_t stop_time, const LocationContainer& location_container,
|
|
||||||
operations_research::RoutingNodeEvaluator2 transition_time);
|
|
||||||
int64_t Compute(RoutingIndexManager::NodeIndex from,
|
|
||||||
RoutingIndexManager::NodeIndex to) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const int64_t stop_time_;
|
|
||||||
const LocationContainer& location_container_;
|
|
||||||
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_t max_nodes_per_group, int64_t same_vehicle_cost,
|
|
||||||
const operations_research::RoutingDimension& capacity_dimension,
|
|
||||||
const operations_research::RoutingDimension& time_dimension);
|
|
||||||
|
|
||||||
using NodeIndex = RoutingIndexManager::NodeIndex;
|
|
||||||
|
|
||||||
int32_t GetSeed(bool deterministic) {
|
|
||||||
if (deterministic) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return std::random_device()();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationContainer::LocationContainer(int64_t speed, bool use_deterministic_seed)
|
|
||||||
: randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) {
|
|
||||||
CHECK_LT(0, speed_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LocationContainer::AddRandomLocation(int64_t x_max, int64_t y_max) {
|
|
||||||
AddRandomLocation(x_max, y_max, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LocationContainer::AddRandomLocation(int64_t x_max, int64_t y_max,
|
|
||||||
int duplicates) {
|
|
||||||
const int64_t x = absl::Uniform(randomizer_, 0, x_max + 1);
|
|
||||||
const int64_t y = absl::Uniform(randomizer_, 0, y_max + 1);
|
|
||||||
for (int i = 0; i < duplicates; ++i) {
|
|
||||||
AddLocation(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t LocationContainer::ManhattanDistance(NodeIndex from,
|
|
||||||
NodeIndex to) const {
|
|
||||||
return locations_[from].DistanceTo(locations_[to]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t LocationContainer::NegManhattanDistance(NodeIndex from,
|
|
||||||
NodeIndex to) const {
|
|
||||||
return -ManhattanDistance(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t LocationContainer::ManhattanTime(NodeIndex from, NodeIndex to) const {
|
|
||||||
return ManhattanDistance(from, to) / speed_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocationContainer::SameLocation(NodeIndex node1, NodeIndex node2) const {
|
|
||||||
if (node1 < locations_.size() && node2 < locations_.size()) {
|
|
||||||
return locations_[node1].IsAtSameLocation(locations_[node2]);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int64_t LocationContainer::SameLocationFromIndex(int64_t node1,
|
|
||||||
int64_t node2) const {
|
|
||||||
// The direct conversion from constraint model indices to routing model
|
|
||||||
// nodes is correct because the depot is node 0.
|
|
||||||
// TODO(user): Fetch proper indices from routing model.
|
|
||||||
return SameLocation(NodeIndex(node1), NodeIndex(node2));
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationContainer::Location::Location() : x_(0), y_(0) {}
|
|
||||||
|
|
||||||
LocationContainer::Location::Location(int64_t x, int64_t y) : x_(x), y_(y) {}
|
|
||||||
|
|
||||||
int64_t LocationContainer::Location::DistanceTo(
|
|
||||||
const Location& location) const {
|
|
||||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocationContainer::Location::IsAtSameLocation(
|
|
||||||
const Location& location) const {
|
|
||||||
return x_ == location.x_ && y_ == location.y_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t LocationContainer::Location::Abs(int64_t value) {
|
|
||||||
return std::max(value, -value);
|
|
||||||
}
|
|
||||||
|
|
||||||
RandomDemand::RandomDemand(int size, NodeIndex depot,
|
|
||||||
bool use_deterministic_seed)
|
|
||||||
: size_(size),
|
|
||||||
depot_(depot),
|
|
||||||
use_deterministic_seed_(use_deterministic_seed) {
|
|
||||||
CHECK_LT(0, size_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RandomDemand::Initialize() {
|
|
||||||
const int64_t kDemandMax = 5;
|
|
||||||
const int64_t kDemandMin = 1;
|
|
||||||
demand_ = absl::make_unique<int64_t[]>(size_);
|
|
||||||
random_engine_t randomizer;
|
|
||||||
for (int order = 0; order < size_; ++order) {
|
|
||||||
if (order == depot_) {
|
|
||||||
demand_[order] = 0;
|
|
||||||
} else {
|
|
||||||
demand_[order] = kDemandMin + absl::Uniform(randomizer, 0,
|
|
||||||
kDemandMax - kDemandMin + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t RandomDemand::Demand(NodeIndex from, NodeIndex /*to*/) const {
|
|
||||||
return demand_[from.value()];
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceTimePlusTransition::ServiceTimePlusTransition(
|
|
||||||
int64_t time_per_demand_unit, RoutingNodeEvaluator2 demand,
|
|
||||||
RoutingNodeEvaluator2 transition_time)
|
|
||||||
: time_per_demand_unit_(time_per_demand_unit),
|
|
||||||
demand_(std::move(demand)),
|
|
||||||
transition_time_(std::move(transition_time)) {}
|
|
||||||
|
|
||||||
int64_t ServiceTimePlusTransition::Compute(NodeIndex from, NodeIndex to) const {
|
|
||||||
return time_per_demand_unit_ * demand_(from, to) + transition_time_(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
StopServiceTimePlusTransition::StopServiceTimePlusTransition(
|
|
||||||
int64_t stop_time, const LocationContainer& location_container,
|
|
||||||
RoutingNodeEvaluator2 transition_time)
|
|
||||||
: stop_time_(stop_time),
|
|
||||||
location_container_(location_container),
|
|
||||||
transition_time_(std::move(transition_time)) {}
|
|
||||||
|
|
||||||
int64_t StopServiceTimePlusTransition::Compute(NodeIndex from,
|
|
||||||
NodeIndex to) const {
|
|
||||||
return location_container_.SameLocation(from, to)
|
|
||||||
? 0
|
|
||||||
: stop_time_ + transition_time_(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayPlan(
|
|
||||||
const RoutingIndexManager& manager, const RoutingModel& routing,
|
|
||||||
const operations_research::Assignment& plan, bool use_same_vehicle_costs,
|
|
||||||
int64_t max_nodes_per_group, int64_t same_vehicle_cost,
|
|
||||||
const operations_research::RoutingDimension& capacity_dimension,
|
|
||||||
const operations_research::RoutingDimension& time_dimension) {
|
|
||||||
// Display plan cost.
|
|
||||||
std::string plan_output = absl::StrFormat("Cost %d\n", plan.ObjectiveValue());
|
|
||||||
|
|
||||||
// Display dropped orders.
|
|
||||||
std::string dropped;
|
|
||||||
for (int64_t order = 0; order < routing.Size(); ++order) {
|
|
||||||
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
|
|
||||||
if (plan.Value(routing.NextVar(order)) == order) {
|
|
||||||
if (dropped.empty()) {
|
|
||||||
absl::StrAppendFormat(&dropped, " %d",
|
|
||||||
manager.IndexToNode(order).value());
|
|
||||||
} else {
|
|
||||||
absl::StrAppendFormat(&dropped, ", %d",
|
|
||||||
manager.IndexToNode(order).value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!dropped.empty()) {
|
|
||||||
plan_output += "Dropped orders:" + dropped + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (use_same_vehicle_costs) {
|
|
||||||
int group_size = 0;
|
|
||||||
int64_t group_same_vehicle_cost = 0;
|
|
||||||
std::set<int> visited;
|
|
||||||
for (int64_t order = 0; order < routing.Size(); ++order) {
|
|
||||||
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
|
|
||||||
++group_size;
|
|
||||||
visited.insert(plan.Value(routing.VehicleVar(order)));
|
|
||||||
if (group_size == max_nodes_per_group) {
|
|
||||||
if (visited.size() > 1) {
|
|
||||||
group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost;
|
|
||||||
}
|
|
||||||
group_size = 0;
|
|
||||||
visited.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (visited.size() > 1) {
|
|
||||||
group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost;
|
|
||||||
}
|
|
||||||
LOG(INFO) << "Same vehicle costs: " << group_same_vehicle_cost;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display actual output for each vehicle.
|
|
||||||
for (int route_number = 0; route_number < routing.vehicles();
|
|
||||||
++route_number) {
|
|
||||||
int64_t order = routing.Start(route_number);
|
|
||||||
absl::StrAppendFormat(&plan_output, "Route %d: ", route_number);
|
|
||||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
|
||||||
plan_output += "Empty\n";
|
|
||||||
} else {
|
|
||||||
while (true) {
|
|
||||||
operations_research::IntVar* const load_var =
|
|
||||||
capacity_dimension.CumulVar(order);
|
|
||||||
operations_research::IntVar* const time_var =
|
|
||||||
time_dimension.CumulVar(order);
|
|
||||||
operations_research::IntVar* const slack_var =
|
|
||||||
routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order);
|
|
||||||
if (slack_var != nullptr && plan.Contains(slack_var)) {
|
|
||||||
absl::StrAppendFormat(
|
|
||||||
&plan_output, "%d Load(%d) Time(%d, %d) Slack(%d, %d)",
|
|
||||||
manager.IndexToNode(order).value(), plan.Value(load_var),
|
|
||||||
plan.Min(time_var), plan.Max(time_var), plan.Min(slack_var),
|
|
||||||
plan.Max(slack_var));
|
|
||||||
} else {
|
|
||||||
absl::StrAppendFormat(&plan_output, "%d Load(%d) Time(%d, %d)",
|
|
||||||
manager.IndexToNode(order).value(),
|
|
||||||
plan.Value(load_var), plan.Min(time_var),
|
|
||||||
plan.Max(time_var));
|
|
||||||
}
|
|
||||||
if (routing.IsEnd(order)) break;
|
|
||||||
plan_output += " -> ";
|
|
||||||
order = plan.Value(routing.NextVar(order));
|
|
||||||
}
|
|
||||||
plan_output += "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG(INFO) << plan_output;
|
|
||||||
}
|
|
||||||
} // namespace operations_research
|
|
||||||
|
|
||||||
#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
// Copyright 2010-2024 Google LLC
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//
|
|
||||||
// Capacitated Vehicle Routing Problem with Time Windows and Breaks.
|
|
||||||
// A description of the Capacitated Vehicle Routing Problem with Time Windows
|
|
||||||
// can be found here:
|
|
||||||
// http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
|
||||||
// The variant which is tackled by this model includes a capacity dimension,
|
|
||||||
// time windows and optional orders, with a penalty cost if orders are not
|
|
||||||
// performed. For the sake of simplicty, orders are randomly located and
|
|
||||||
// distances are computed using the Manhattan distance. Distances are assumed
|
|
||||||
// to be in meters and times in seconds.
|
|
||||||
// This variant also includes vehicle breaks which must happen during the day
|
|
||||||
// with two alternate breaks schemes: either a long break in the middle of the
|
|
||||||
// day or two smaller ones which can be taken during a longer period of the day.
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "absl/random/random.h"
|
|
||||||
#include "absl/strings/str_cat.h"
|
|
||||||
#include "examples/cpp/cvrptw_lib.h"
|
|
||||||
#include "google/protobuf/text_format.h"
|
|
||||||
#include "ortools/base/commandlineflags.h"
|
|
||||||
#include "ortools/base/init_google.h"
|
|
||||||
#include "ortools/base/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_index_manager.h"
|
|
||||||
#include "ortools/constraint_solver/routing_parameters.h"
|
|
||||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem.");
|
|
||||||
ABSL_FLAG(int, vrp_vehicles, 20,
|
|
||||||
"Size of Traveling Salesman Problem instance.");
|
|
||||||
ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
|
|
||||||
"Use deterministic random seeds.");
|
|
||||||
ABSL_FLAG(std::string, routing_search_parameters, "",
|
|
||||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
|
||||||
"override the DefaultRoutingSearchParameters()");
|
|
||||||
|
|
||||||
const char* kTime = "Time";
|
|
||||||
const char* kCapacity = "Capacity";
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
InitGoogle(argv[0], &argc, &argv, true);
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
|
||||||
<< "Specify an instance size greater than 0.";
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
|
||||||
<< "Specify a non-null vehicle fleet size.";
|
|
||||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
|
||||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
|
||||||
// ends of the routes are at node 0.
|
|
||||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
|
||||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
|
||||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
|
||||||
RoutingModel routing(manager);
|
|
||||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
|
||||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
|
||||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
|
||||||
parameters.set_first_solution_strategy(
|
|
||||||
FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION);
|
|
||||||
|
|
||||||
// Setting up locations.
|
|
||||||
const int64_t kXMax = 100000;
|
|
||||||
const int64_t kYMax = 100000;
|
|
||||||
const int64_t kSpeed = 10;
|
|
||||||
LocationContainer locations(
|
|
||||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
|
||||||
++location) {
|
|
||||||
locations.AddRandomLocation(kXMax, kYMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the cost function.
|
|
||||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
|
||||||
[&locations, &manager](int64_t i, int64_t j) {
|
|
||||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
|
||||||
manager.IndexToNode(j));
|
|
||||||
});
|
|
||||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
|
||||||
|
|
||||||
// Adding capacity dimension constraints.
|
|
||||||
const int64_t kVehicleCapacity = 40;
|
|
||||||
const int64_t kNullCapacitySlack = 0;
|
|
||||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
demand.Initialize();
|
|
||||||
routing.AddDimension(routing.RegisterTransitCallback(
|
|
||||||
[&demand, &manager](int64_t i, int64_t 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_t kTimePerDemandUnit = 300;
|
|
||||||
const int64_t kHorizon = 24 * 3600;
|
|
||||||
ServiceTimePlusTransition time(
|
|
||||||
kTimePerDemandUnit,
|
|
||||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return demand.Demand(i, j);
|
|
||||||
},
|
|
||||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return locations.ManhattanTime(i, j);
|
|
||||||
});
|
|
||||||
routing.AddDimension(
|
|
||||||
routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) {
|
|
||||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
|
||||||
}),
|
|
||||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
|
||||||
RoutingDimension* const time_dimension = routing.GetMutableDimension(kTime);
|
|
||||||
|
|
||||||
// Adding time windows.
|
|
||||||
std::mt19937 randomizer(
|
|
||||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
|
||||||
const int64_t kTWDuration = 5 * 3600;
|
|
||||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
|
||||||
const int64_t start =
|
|
||||||
absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
|
|
||||||
time_dimension->CumulVar(order)->SetRange(start, start + kTWDuration);
|
|
||||||
routing.AddToAssignment(time_dimension->SlackVar(order));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minimize time variables.
|
|
||||||
for (int i = 0; i < routing.Size(); ++i) {
|
|
||||||
routing.AddVariableMinimizedByFinalizer(time_dimension->CumulVar(i));
|
|
||||||
}
|
|
||||||
for (int j = 0; j < absl::GetFlag(FLAGS_vrp_vehicles); ++j) {
|
|
||||||
routing.AddVariableMinimizedByFinalizer(
|
|
||||||
time_dimension->CumulVar(routing.Start(j)));
|
|
||||||
routing.AddVariableMinimizedByFinalizer(
|
|
||||||
time_dimension->CumulVar(routing.End(j)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding vehicle breaks:
|
|
||||||
// - 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_t> service_times(routing.Size());
|
|
||||||
for (int node = 0; node < routing.Size(); node++) {
|
|
||||||
if (node >= routing.nodes()) {
|
|
||||||
service_times[node] = 0;
|
|
||||||
} else {
|
|
||||||
const RoutingIndexManager::NodeIndex index(node);
|
|
||||||
service_times[node] = kTimePerDemandUnit * demand.Demand(index, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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}};
|
|
||||||
Solver* const solver = routing.solver();
|
|
||||||
for (int vehicle = 0; vehicle < absl::GetFlag(FLAGS_vrp_vehicles);
|
|
||||||
++vehicle) {
|
|
||||||
std::vector<IntervalVar*> breaks;
|
|
||||||
for (int i = 0; i < break_data.size(); ++i) {
|
|
||||||
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
|
|
||||||
solver->AddConstraint(solver->MakeEquality(breaks[1]->PerformedExpr(),
|
|
||||||
breaks[2]->PerformedExpr()));
|
|
||||||
// break2 start 1h after break1.
|
|
||||||
solver->AddConstraint(solver->MakeIntervalVarRelationWithDelay(
|
|
||||||
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,
|
|
||||||
service_times);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding penalty costs to allow skipping orders.
|
|
||||||
const int64_t kPenalty = 10000000;
|
|
||||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
|
||||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
|
||||||
order < routing.nodes(); ++order) {
|
|
||||||
std::vector<int64_t> orders(1, manager.NodeToIndex(order));
|
|
||||||
routing.AddDisjunction(orders, kPenalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve, returns a solution if any (owned by RoutingModel).
|
|
||||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
|
||||||
if (solution != nullptr) {
|
|
||||||
LOG(INFO) << "Breaks: ";
|
|
||||||
for (const auto& break_interval :
|
|
||||||
solution->IntervalVarContainer().elements()) {
|
|
||||||
if (break_interval.PerformedValue() == 1) {
|
|
||||||
LOG(INFO) << break_interval.Var()->name() << " "
|
|
||||||
<< break_interval.DebugString();
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << break_interval.Var()->name() << " unperformed";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DisplayPlan(manager, routing, *solution, false, 0, 0,
|
|
||||||
routing.GetDimensionOrDie(kCapacity),
|
|
||||||
routing.GetDimensionOrDie(kTime));
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << "No solution found.";
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
// Copyright 2010-2024 Google LLC
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Capacitated Vehicle Routing Problem with Time Windows and refueling
|
|
||||||
// constraints.
|
|
||||||
// This is an extension to the model in cvrptw.cc so refer to that file for
|
|
||||||
// more information on the common part of the model. The model implemented here
|
|
||||||
// takes into account refueling constraints using a specific dimension: vehicles
|
|
||||||
// must visit certain nodes (refueling nodes) before the quantity of fuel
|
|
||||||
// reaches zero. Fuel consumption is proportional to the distance traveled.
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "absl/random/random.h"
|
|
||||||
#include "examples/cpp/cvrptw_lib.h"
|
|
||||||
#include "google/protobuf/text_format.h"
|
|
||||||
#include "ortools/base/commandlineflags.h"
|
|
||||||
#include "ortools/base/init_google.h"
|
|
||||||
#include "ortools/base/types.h"
|
|
||||||
#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"
|
|
||||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem.");
|
|
||||||
ABSL_FLAG(int, vrp_vehicles, 20,
|
|
||||||
"Size of Traveling Salesman Problem instance.");
|
|
||||||
ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
|
|
||||||
"Use deterministic random seeds.");
|
|
||||||
ABSL_FLAG(std::string, routing_search_parameters, "",
|
|
||||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
|
||||||
"override the DefaultRoutingSearchParameters()");
|
|
||||||
|
|
||||||
const char* kTime = "Time";
|
|
||||||
const char* kCapacity = "Capacity";
|
|
||||||
const char* kFuel = "Fuel";
|
|
||||||
|
|
||||||
// Returns true if node is a refueling node (based on node / refuel node ratio).
|
|
||||||
bool IsRefuelNode(int64_t node) {
|
|
||||||
const int64_t kRefuelNodeRatio = 10;
|
|
||||||
return (node % kRefuelNodeRatio == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
InitGoogle(argv[0], &argc, &argv, true);
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
|
||||||
<< "Specify an instance size greater than 0.";
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
|
||||||
<< "Specify a non-null vehicle fleet size.";
|
|
||||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
|
||||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
|
||||||
// ends of the routes are at node 0.
|
|
||||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
|
||||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
|
||||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
|
||||||
RoutingModel routing(manager);
|
|
||||||
|
|
||||||
// Setting up locations.
|
|
||||||
const int64_t kXMax = 100000;
|
|
||||||
const int64_t kYMax = 100000;
|
|
||||||
const int64_t kSpeed = 10;
|
|
||||||
LocationContainer locations(
|
|
||||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
|
||||||
++location) {
|
|
||||||
locations.AddRandomLocation(kXMax, kYMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the cost function.
|
|
||||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
|
||||||
[&locations, &manager](int64_t i, int64_t j) {
|
|
||||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
|
||||||
manager.IndexToNode(j));
|
|
||||||
});
|
|
||||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
|
||||||
|
|
||||||
// Adding capacity dimension constraints.
|
|
||||||
const int64_t kVehicleCapacity = 40;
|
|
||||||
const int64_t kNullCapacitySlack = 0;
|
|
||||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
demand.Initialize();
|
|
||||||
routing.AddDimension(routing.RegisterTransitCallback(
|
|
||||||
[&demand, &manager](int64_t i, int64_t 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_t kTimePerDemandUnit = 300;
|
|
||||||
const int64_t kHorizon = 24 * 3600;
|
|
||||||
ServiceTimePlusTransition time(
|
|
||||||
kTimePerDemandUnit,
|
|
||||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return demand.Demand(i, j);
|
|
||||||
},
|
|
||||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return locations.ManhattanTime(i, j);
|
|
||||||
});
|
|
||||||
routing.AddDimension(
|
|
||||||
routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) {
|
|
||||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
|
||||||
}),
|
|
||||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
|
||||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
|
||||||
// Adding time windows.
|
|
||||||
// NOTE(user): This randomized test case is quite sensible to the seed:
|
|
||||||
// the generated model can be much easier or harder to solve, depending on
|
|
||||||
// the seed. It turns out that most seeds yield pretty slow/bad solver
|
|
||||||
// performance: I got good performance for about 10% of the seeds.
|
|
||||||
std::mt19937 randomizer(
|
|
||||||
144 + GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
|
||||||
const int64_t kTWDuration = 5 * 3600;
|
|
||||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
|
||||||
if (!IsRefuelNode(order)) {
|
|
||||||
const int64_t start =
|
|
||||||
absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
|
|
||||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding fuel dimension. This dimension consumes a quantity equal to the
|
|
||||||
// distance traveled. Only refuel nodes can make the quantity of dimension
|
|
||||||
// increase by letting slack variable replenish the fuel.
|
|
||||||
const int64_t kFuelCapacity = kXMax + kYMax;
|
|
||||||
routing.AddDimension(
|
|
||||||
routing.RegisterTransitCallback(
|
|
||||||
[&locations, &manager](int64_t i, int64_t j) {
|
|
||||||
return locations.NegManhattanDistance(manager.IndexToNode(i),
|
|
||||||
manager.IndexToNode(j));
|
|
||||||
}),
|
|
||||||
kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, 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)) {
|
|
||||||
fuel_dimension.SlackVar(order)->SetValue(0);
|
|
||||||
}
|
|
||||||
// Needed to instantiate fuel quantity at each node.
|
|
||||||
routing.AddVariableMinimizedByFinalizer(fuel_dimension.CumulVar(order));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding penalty costs to allow skipping orders.
|
|
||||||
const int64_t kPenalty = 100000;
|
|
||||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
|
||||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
|
||||||
order < routing.nodes(); ++order) {
|
|
||||||
std::vector<int64_t> orders(1, manager.NodeToIndex(order));
|
|
||||||
routing.AddDisjunction(orders, kPenalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve, returns a solution if any (owned by RoutingModel).
|
|
||||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
|
||||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
|
||||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
|
||||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
|
||||||
if (solution != nullptr) {
|
|
||||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
|
||||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
|
||||||
routing.GetDimensionOrDie(kCapacity),
|
|
||||||
routing.GetDimensionOrDie(kTime));
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << "No solution found.";
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// Copyright 2010-2024 Google LLC
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Capacitated Vehicle Routing Problem with Time Windows and capacitated
|
|
||||||
// resources.
|
|
||||||
// This is an extension to the model in cvrptw.cc so refer to that file for
|
|
||||||
// more information on the common part of the model. The model implemented here
|
|
||||||
// limits the number of vehicles which can simultaneously leave or enter the
|
|
||||||
// depot due to limited resources (or capacity) available.
|
|
||||||
// TODO(user): The current model consumes resources even for vehicles with
|
|
||||||
// empty routes; fix this when we have an API on the cumulative constraints
|
|
||||||
// with variable demands.
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "absl/random/random.h"
|
|
||||||
#include "examples/cpp/cvrptw_lib.h"
|
|
||||||
#include "google/protobuf/text_format.h"
|
|
||||||
#include "ortools/base/commandlineflags.h"
|
|
||||||
#include "ortools/base/init_google.h"
|
|
||||||
#include "ortools/base/types.h"
|
|
||||||
#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"
|
|
||||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
|
||||||
|
|
||||||
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::Solver;
|
|
||||||
|
|
||||||
ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem.");
|
|
||||||
ABSL_FLAG(int, vrp_vehicles, 20,
|
|
||||||
"Size of Traveling Salesman Problem instance.");
|
|
||||||
ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
|
|
||||||
"Use deterministic random seeds.");
|
|
||||||
ABSL_FLAG(std::string, routing_search_parameters, "",
|
|
||||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
|
||||||
"override the DefaultRoutingSearchParameters()");
|
|
||||||
|
|
||||||
const char* kTime = "Time";
|
|
||||||
const char* kCapacity = "Capacity";
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
InitGoogle(argv[0], &argc, &argv, true);
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
|
||||||
<< "Specify an instance size greater than 0.";
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
|
||||||
<< "Specify a non-null vehicle fleet size.";
|
|
||||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
|
||||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
|
||||||
// ends of the routes are at node 0.
|
|
||||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
|
||||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
|
||||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
|
||||||
RoutingModel routing(manager);
|
|
||||||
|
|
||||||
// Setting up locations.
|
|
||||||
const int64_t kXMax = 100000;
|
|
||||||
const int64_t kYMax = 100000;
|
|
||||||
const int64_t kSpeed = 10;
|
|
||||||
LocationContainer locations(
|
|
||||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
|
||||||
++location) {
|
|
||||||
locations.AddRandomLocation(kXMax, kYMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the cost function.
|
|
||||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
|
||||||
[&locations, &manager](int64_t i, int64_t j) {
|
|
||||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
|
||||||
manager.IndexToNode(j));
|
|
||||||
});
|
|
||||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
|
||||||
|
|
||||||
// Adding capacity dimension constraints.
|
|
||||||
const int64_t kVehicleCapacity = 40;
|
|
||||||
const int64_t kNullCapacitySlack = 0;
|
|
||||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
demand.Initialize();
|
|
||||||
routing.AddDimension(routing.RegisterTransitCallback(
|
|
||||||
[&demand, &manager](int64_t i, int64_t 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_t kTimePerDemandUnit = 300;
|
|
||||||
const int64_t kHorizon = 24 * 3600;
|
|
||||||
ServiceTimePlusTransition time(
|
|
||||||
kTimePerDemandUnit,
|
|
||||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return demand.Demand(i, j);
|
|
||||||
},
|
|
||||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return locations.ManhattanTime(i, j);
|
|
||||||
});
|
|
||||||
routing.AddDimension(
|
|
||||||
routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t 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.
|
|
||||||
std::mt19937 randomizer(
|
|
||||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
|
||||||
const int64_t kTWDuration = 5 * 3600;
|
|
||||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
|
||||||
const int64_t start =
|
|
||||||
absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
|
|
||||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding resource constraints at the depot (start and end location of
|
|
||||||
// routes).
|
|
||||||
std::vector<IntVar*> start_end_times;
|
|
||||||
for (int i = 0; i < absl::GetFlag(FLAGS_vrp_vehicles); ++i) {
|
|
||||||
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_t kVehicleSetup = 180;
|
|
||||||
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.
|
|
||||||
const int64_t kDepotCapacity = 5;
|
|
||||||
std::vector<int64_t> depot_usage(start_end_times.size(), 1);
|
|
||||||
solver->AddConstraint(
|
|
||||||
solver->MakeCumulative(intervals, depot_usage, kDepotCapacity, "depot"));
|
|
||||||
// Instantiate route start and end times to produce feasible times.
|
|
||||||
for (int i = 0; i < start_end_times.size(); ++i) {
|
|
||||||
routing.AddVariableMinimizedByFinalizer(start_end_times[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding penalty costs to allow skipping orders.
|
|
||||||
const int64_t kPenalty = 100000;
|
|
||||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
|
||||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
|
||||||
order < manager.num_nodes(); ++order) {
|
|
||||||
std::vector<int64_t> orders(1, manager.NodeToIndex(order));
|
|
||||||
routing.AddDisjunction(orders, kPenalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve, returns a solution if any (owned by RoutingModel).
|
|
||||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
|
||||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
|
||||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
|
||||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
|
||||||
if (solution != nullptr) {
|
|
||||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
|
||||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
|
||||||
routing.GetDimensionOrDie(kCapacity),
|
|
||||||
routing.GetDimensionOrDie(kTime));
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << "No solution found.";
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
// Copyright 2010-2024 Google LLC
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Capacitated Vehicle Routing Problem with Time Windows, fixed stop times and
|
|
||||||
// capacitated resources. A stop is defined as consecutive nodes at the same
|
|
||||||
// location.
|
|
||||||
// This is an extension to the model in cvrptw.cc so refer to that file for
|
|
||||||
// more information on the common part of the model. The model implemented here
|
|
||||||
// limits the number of vehicles which can simultaneously leave or enter a node
|
|
||||||
// to one.
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "absl/random/random.h"
|
|
||||||
#include "absl/strings/str_cat.h"
|
|
||||||
#include "examples/cpp/cvrptw_lib.h"
|
|
||||||
#include "google/protobuf/text_format.h"
|
|
||||||
#include "ortools/base/commandlineflags.h"
|
|
||||||
#include "ortools/base/init_google.h"
|
|
||||||
#include "ortools/base/types.h"
|
|
||||||
#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"
|
|
||||||
#include "ortools/constraint_solver/routing_parameters.pb.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
ABSL_FLAG(int, vrp_stops, 25, "Stop locations in the problem.");
|
|
||||||
ABSL_FLAG(int, vrp_orders_per_stop, 5, "Nodes for each stop.");
|
|
||||||
ABSL_FLAG(int, vrp_vehicles, 20,
|
|
||||||
"Size of Traveling Salesman Problem instance.");
|
|
||||||
ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
|
|
||||||
"Use deterministic random seeds.");
|
|
||||||
ABSL_FLAG(std::string, routing_search_parameters, "",
|
|
||||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
|
||||||
"override the DefaultRoutingSearchParameters()");
|
|
||||||
|
|
||||||
const char* kTime = "Time";
|
|
||||||
const char* kCapacity = "Capacity";
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
InitGoogle(argv[0], &argc, &argv, true);
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_stops))
|
|
||||||
<< "Specify an instance size greater than 0.";
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders_per_stop))
|
|
||||||
<< "Specify an instance size greater than 0.";
|
|
||||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
|
||||||
<< "Specify a non-null vehicle fleet size.";
|
|
||||||
const int vrp_orders =
|
|
||||||
absl::GetFlag(FLAGS_vrp_stops) * absl::GetFlag(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 RoutingIndexManager::NodeIndex kDepot(0);
|
|
||||||
RoutingIndexManager manager(vrp_orders + 1, absl::GetFlag(FLAGS_vrp_vehicles),
|
|
||||||
kDepot);
|
|
||||||
RoutingModel routing(manager);
|
|
||||||
|
|
||||||
// Setting up locations.
|
|
||||||
const int64_t kXMax = 100000;
|
|
||||||
const int64_t kYMax = 100000;
|
|
||||||
const int64_t kSpeed = 10;
|
|
||||||
LocationContainer locations(
|
|
||||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
for (int stop = 0; stop <= absl::GetFlag(FLAGS_vrp_stops); ++stop) {
|
|
||||||
const int num_orders =
|
|
||||||
stop == 0 ? 1 : absl::GetFlag(FLAGS_vrp_orders_per_stop);
|
|
||||||
locations.AddRandomLocation(kXMax, kYMax, num_orders);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the cost function.
|
|
||||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
|
||||||
[&locations, &manager](int64_t i, int64_t j) {
|
|
||||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
|
||||||
manager.IndexToNode(j));
|
|
||||||
});
|
|
||||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
|
||||||
|
|
||||||
// Adding capacity dimension constraints.
|
|
||||||
const int64_t kVehicleCapacity = 40;
|
|
||||||
const int64_t kNullCapacitySlack = 0;
|
|
||||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
|
||||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
|
||||||
demand.Initialize();
|
|
||||||
routing.AddDimension(routing.RegisterTransitCallback(
|
|
||||||
[&demand, &manager](int64_t i, int64_t 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_t kStopTime = 300;
|
|
||||||
const int64_t kHorizon = 24 * 3600;
|
|
||||||
StopServiceTimePlusTransition time(
|
|
||||||
kStopTime, locations,
|
|
||||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
|
||||||
return locations.ManhattanTime(i, j);
|
|
||||||
});
|
|
||||||
routing.AddDimension(
|
|
||||||
routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t 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.
|
|
||||||
std::mt19937 randomizer(
|
|
||||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
|
||||||
const int64_t kTWDuration = 5 * 3600;
|
|
||||||
for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) {
|
|
||||||
const int64_t start =
|
|
||||||
absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
|
|
||||||
for (int stop_order = 0;
|
|
||||||
stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) {
|
|
||||||
const int order =
|
|
||||||
stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1;
|
|
||||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding resource constraints at order locations.
|
|
||||||
Solver* const solver = routing.solver();
|
|
||||||
std::vector<IntervalVar*> intervals;
|
|
||||||
for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) {
|
|
||||||
std::vector<IntervalVar*> stop_intervals;
|
|
||||||
for (int stop_order = 0;
|
|
||||||
stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) {
|
|
||||||
const int order =
|
|
||||||
stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1;
|
|
||||||
IntervalVar* const interval = solver->MakeFixedDurationIntervalVar(
|
|
||||||
0, kHorizon, kStopTime, true, absl::StrCat("Order", order));
|
|
||||||
intervals.push_back(interval);
|
|
||||||
stop_intervals.push_back(interval);
|
|
||||||
// Link order and interval.
|
|
||||||
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.
|
|
||||||
IntVar* const is_null_duration =
|
|
||||||
solver
|
|
||||||
->MakeElement(
|
|
||||||
[&locations, order](int64_t index) {
|
|
||||||
return locations.SameLocationFromIndex(order, index);
|
|
||||||
},
|
|
||||||
routing.NextVar(order))
|
|
||||||
->Var();
|
|
||||||
solver->AddConstraint(
|
|
||||||
solver->MakeNonEquality(interval->PerformedExpr(), is_null_duration));
|
|
||||||
routing.AddIntervalToAssignment(interval);
|
|
||||||
// We are minimizing route durations by minimizing route ends; so we can
|
|
||||||
// maximize order starts to pack them together.
|
|
||||||
routing.AddVariableMaximizedByFinalizer(order_start);
|
|
||||||
}
|
|
||||||
// Only one order can happen at the same time at a given location.
|
|
||||||
std::vector<int64_t> location_usage(stop_intervals.size(), 1);
|
|
||||||
solver->AddConstraint(solver->MakeCumulative(
|
|
||||||
stop_intervals, location_usage, 1, absl::StrCat("Client", stop)));
|
|
||||||
}
|
|
||||||
// Minimizing route duration.
|
|
||||||
for (int vehicle = 0; vehicle < manager.num_vehicles(); ++vehicle) {
|
|
||||||
routing.AddVariableMinimizedByFinalizer(
|
|
||||||
time_dimension.CumulVar(routing.End(vehicle)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding penalty costs to allow skipping orders.
|
|
||||||
const int64_t kPenalty = 100000;
|
|
||||||
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
|
|
||||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
|
||||||
order < routing.nodes(); ++order) {
|
|
||||||
std::vector<int64_t> orders(1, manager.NodeToIndex(order));
|
|
||||||
routing.AddDisjunction(orders, kPenalty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve, returns a solution if any (owned by RoutingModel).
|
|
||||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
|
||||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
|
||||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
|
||||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
|
||||||
if (solution != nullptr) {
|
|
||||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
|
||||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
|
||||||
routing.GetDimensionOrDie(kCapacity),
|
|
||||||
routing.GetDimensionOrDie(kTime));
|
|
||||||
LOG(INFO) << "Stop intervals:";
|
|
||||||
for (IntervalVar* const interval : intervals) {
|
|
||||||
if (solution->PerformedValue(interval)) {
|
|
||||||
LOG(INFO) << interval->name() << ": " << solution->StartValue(interval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG(INFO) << "No solution found.";
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -54,6 +54,7 @@
|
|||||||
|
|
||||||
#include "absl/container/btree_map.h"
|
#include "absl/container/btree_map.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
|
#include "absl/types/span.h"
|
||||||
#include "examples/cpp/fap_model_printer.h"
|
#include "examples/cpp/fap_model_printer.h"
|
||||||
#include "examples/cpp/fap_parser.h"
|
#include "examples/cpp/fap_parser.h"
|
||||||
#include "examples/cpp/fap_utilities.h"
|
#include "examples/cpp/fap_utilities.h"
|
||||||
@@ -100,6 +101,10 @@ class OrderingDecision : public Decision {
|
|||||||
variable2_(variable2),
|
variable2_(variable2),
|
||||||
value_(value),
|
value_(value),
|
||||||
operator_(std::move(operation)) {}
|
operator_(std::move(operation)) {}
|
||||||
|
|
||||||
|
// This type is neither copyable nor movable.
|
||||||
|
OrderingDecision(const OrderingDecision&) = delete;
|
||||||
|
OrderingDecision& operator=(const OrderingDecision&) = delete;
|
||||||
~OrderingDecision() override = default;
|
~OrderingDecision() override = default;
|
||||||
|
|
||||||
// Apply will be called first when the decision is executed.
|
// Apply will be called first when the decision is executed.
|
||||||
@@ -131,8 +136,6 @@ class OrderingDecision : public Decision {
|
|||||||
IntVar* const variable2_;
|
IntVar* const variable2_;
|
||||||
const int value_;
|
const int value_;
|
||||||
const std::string operator_;
|
const std::string operator_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(OrderingDecision);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Decision on whether a soft constraint will be added to a model
|
// Decision on whether a soft constraint will be added to a model
|
||||||
@@ -142,6 +145,10 @@ class ConstraintDecision : public Decision {
|
|||||||
explicit ConstraintDecision(IntVar* const constraint_violation)
|
explicit ConstraintDecision(IntVar* const constraint_violation)
|
||||||
: constraint_violation_(constraint_violation) {}
|
: constraint_violation_(constraint_violation) {}
|
||||||
|
|
||||||
|
// This type is neither copyable nor movable.
|
||||||
|
ConstraintDecision(const ConstraintDecision&) = delete;
|
||||||
|
ConstraintDecision& operator=(const ConstraintDecision&) = delete;
|
||||||
|
|
||||||
~ConstraintDecision() override = default;
|
~ConstraintDecision() override = default;
|
||||||
|
|
||||||
// Apply will be called first when the decision is executed.
|
// Apply will be called first when the decision is executed.
|
||||||
@@ -158,8 +165,6 @@ class ConstraintDecision : public Decision {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
IntVar* const constraint_violation_;
|
IntVar* const constraint_violation_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ConstraintDecision);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The ordering builder resolves the relative order of the two variables
|
// The ordering builder resolves the relative order of the two variables
|
||||||
@@ -192,6 +197,10 @@ class OrderingBuilder : public DecisionBuilder {
|
|||||||
CHECK_EQ(variable_state_.size(), variables_.size());
|
CHECK_EQ(variable_state_.size(), variables_.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This type is neither copyable nor movable.
|
||||||
|
OrderingBuilder(const OrderingBuilder&) = delete;
|
||||||
|
OrderingBuilder& operator=(const OrderingBuilder&) = delete;
|
||||||
|
|
||||||
~OrderingBuilder() override = default;
|
~OrderingBuilder() override = default;
|
||||||
|
|
||||||
Decision* Next(Solver* const s) override {
|
Decision* Next(Solver* const s) override {
|
||||||
@@ -320,8 +329,6 @@ class OrderingBuilder : public DecisionBuilder {
|
|||||||
// Used by Hint() for indicating the most probable ordering.
|
// Used by Hint() for indicating the most probable ordering.
|
||||||
std::vector<Order> variable_state_;
|
std::vector<Order> variable_state_;
|
||||||
std::vector<int> minimum_value_available_;
|
std::vector<int> minimum_value_available_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(OrderingBuilder);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A comparator for sorting the constraints depending on their impact.
|
// A comparator for sorting the constraints depending on their impact.
|
||||||
@@ -373,7 +380,7 @@ int64_t ValueEvaluator(
|
|||||||
// The variables which participate in more constraints and have the
|
// The variables which participate in more constraints and have the
|
||||||
// smaller domain should be in higher priority for assignment.
|
// smaller domain should be in higher priority for assignment.
|
||||||
int64_t VariableEvaluator(
|
int64_t VariableEvaluator(
|
||||||
const std::vector<int>& key_from_index,
|
absl::Span<const int> key_from_index,
|
||||||
const absl::btree_map<int, FapVariable>& data_variables,
|
const absl::btree_map<int, FapVariable>& data_variables,
|
||||||
int64_t variable_index) {
|
int64_t variable_index) {
|
||||||
FapVariable variable =
|
FapVariable variable =
|
||||||
@@ -414,7 +421,7 @@ void CreateModelVariables(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates the constraints of the instance from the parsed data.
|
// Creates the constraints of the instance from the parsed data.
|
||||||
void CreateModelConstraints(const std::vector<FapConstraint>& data_constraints,
|
void CreateModelConstraints(absl::Span<const FapConstraint> data_constraints,
|
||||||
const std::vector<IntVar*>& variables,
|
const std::vector<IntVar*>& variables,
|
||||||
const absl::btree_map<int, int>& index_from_key,
|
const absl::btree_map<int, int>& index_from_key,
|
||||||
Solver* solver) {
|
Solver* solver) {
|
||||||
@@ -649,7 +656,7 @@ void SplitVariablesHardSoft(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Splits constraints of the instance to hard and soft.
|
// Splits constraints of the instance to hard and soft.
|
||||||
void SplitConstraintHardSoft(const std::vector<FapConstraint>& data_constraints,
|
void SplitConstraintHardSoft(absl::Span<const FapConstraint> data_constraints,
|
||||||
std::vector<FapConstraint>* hard_constraints,
|
std::vector<FapConstraint>* hard_constraints,
|
||||||
std::vector<FapConstraint>* soft_constraints) {
|
std::vector<FapConstraint>* soft_constraints) {
|
||||||
for (const FapConstraint& ct : data_constraints) {
|
for (const FapConstraint& ct : data_constraints) {
|
||||||
@@ -683,8 +690,8 @@ void PenalizeVariablesViolation(
|
|||||||
|
|
||||||
// Penalize the violation of soft constraints of the instance.
|
// Penalize the violation of soft constraints of the instance.
|
||||||
void PenalizeConstraintsViolation(
|
void PenalizeConstraintsViolation(
|
||||||
const std::vector<FapConstraint>& constraints,
|
absl::Span<const FapConstraint> constraints,
|
||||||
const std::vector<FapConstraint>& soft_constraints,
|
absl::Span<const FapConstraint> soft_constraints,
|
||||||
const absl::btree_map<int, int>& index_from_key,
|
const absl::btree_map<int, int>& index_from_key,
|
||||||
const std::vector<IntVar*>& variables, std::vector<IntVar*>* cost,
|
const std::vector<IntVar*>& variables, std::vector<IntVar*>* cost,
|
||||||
std::vector<IntVar*>* violated_constraints, Solver* solver) {
|
std::vector<IntVar*>* violated_constraints, Solver* solver) {
|
||||||
@@ -733,7 +740,7 @@ void PenalizeConstraintsViolation(
|
|||||||
int SoftFapSolver(const absl::btree_map<int, FapVariable>& data_variables,
|
int SoftFapSolver(const absl::btree_map<int, FapVariable>& data_variables,
|
||||||
const std::vector<FapConstraint>& data_constraints,
|
const std::vector<FapConstraint>& data_constraints,
|
||||||
absl::string_view /*data_objective*/,
|
absl::string_view /*data_objective*/,
|
||||||
const std::vector<int>& /*values*/) {
|
absl::Span<const int> /*values*/) {
|
||||||
Solver solver("SoftFapSolver");
|
Solver solver("SoftFapSolver");
|
||||||
std::vector<SearchMonitor*> monitors;
|
std::vector<SearchMonitor*> monitors;
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ std::vector<std::vector<MachineTaskData>> GetDataPerMachine(
|
|||||||
|
|
||||||
void CreateMachines(
|
void CreateMachines(
|
||||||
const JsspInputProblem& problem,
|
const JsspInputProblem& problem,
|
||||||
const std::vector<std::vector<std::vector<AlternativeTaskData>>>&
|
absl::Span<const std::vector<std::vector<AlternativeTaskData>>>
|
||||||
job_task_to_alternatives,
|
job_task_to_alternatives,
|
||||||
IntervalVar makespan_interval, CpModelBuilder& cp_model) {
|
IntervalVar makespan_interval, CpModelBuilder& cp_model) {
|
||||||
const int num_jobs = problem.jobs_size();
|
const int num_jobs = problem.jobs_size();
|
||||||
@@ -733,12 +733,6 @@ void Solve(const JsspInputProblem& problem) {
|
|||||||
// Setup parameters.
|
// Setup parameters.
|
||||||
SatParameters parameters;
|
SatParameters parameters;
|
||||||
parameters.set_log_search_progress(true);
|
parameters.set_log_search_progress(true);
|
||||||
// Parse the --params flag.
|
|
||||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
|
||||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
|
||||||
absl::GetFlag(FLAGS_params), ¶meters))
|
|
||||||
<< absl::GetFlag(FLAGS_params);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer objective_shaving_search over objective_lb_search.
|
// Prefer objective_shaving_search over objective_lb_search.
|
||||||
if (parameters.num_workers() >= 16 && parameters.num_workers() < 24) {
|
if (parameters.num_workers() >= 16 && parameters.num_workers() < 24) {
|
||||||
@@ -751,6 +745,13 @@ void Solve(const JsspInputProblem& problem) {
|
|||||||
parameters.set_push_all_tasks_toward_start(true);
|
parameters.set_push_all_tasks_toward_start(true);
|
||||||
parameters.set_use_dynamic_precedence_in_disjunctive(true);
|
parameters.set_use_dynamic_precedence_in_disjunctive(true);
|
||||||
|
|
||||||
|
// Parse the --params flag.
|
||||||
|
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||||
|
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||||
|
absl::GetFlag(FLAGS_params), ¶meters))
|
||||||
|
<< absl::GetFlag(FLAGS_params);
|
||||||
|
}
|
||||||
|
|
||||||
const CpSolverResponse response =
|
const CpSolverResponse response =
|
||||||
SolveWithParameters(cp_model.Build(), parameters);
|
SolveWithParameters(cp_model.Build(), parameters);
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ void PrintSolution(absl::Span<const std::vector<int>> data,
|
|||||||
std::cout << last_line << std::endl;
|
std::cout << last_line << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlitherLink(const std::vector<std::vector<int>>& data) {
|
void SlitherLink(absl::Span<const std::vector<int>> data) {
|
||||||
const int num_rows = data.size();
|
const int num_rows = data.size();
|
||||||
const int num_columns = data[0].size();
|
const int num_columns = data[0].size();
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,20 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/flags/flag.h"
|
#include "absl/flags/flag.h"
|
||||||
|
#include "absl/log/check.h"
|
||||||
#include "absl/strings/numbers.h"
|
#include "absl/strings/numbers.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
#include "absl/strings/str_split.h"
|
#include "absl/strings/str_split.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
#include "ortools/base/init_google.h"
|
#include "ortools/base/init_google.h"
|
||||||
#include "ortools/base/logging.h"
|
#include "ortools/base/logging.h"
|
||||||
#include "ortools/sat/cp_model.h"
|
#include "ortools/sat/cp_model.h"
|
||||||
|
#include "ortools/sat/cp_model.pb.h"
|
||||||
|
#include "ortools/sat/cp_model_solver.h"
|
||||||
#include "ortools/sat/model.h"
|
#include "ortools/sat/model.h"
|
||||||
|
#include "ortools/sat/sat_parameters.pb.h"
|
||||||
#include "ortools/util/filelineiter.h"
|
#include "ortools/util/filelineiter.h"
|
||||||
|
#include "ortools/util/sorted_interval_list.h"
|
||||||
|
|
||||||
ABSL_FLAG(std::string, input, "examples/cpp/wt40.txt", "wt data file name.");
|
ABSL_FLAG(std::string, input, "examples/cpp/wt40.txt", "wt data file name.");
|
||||||
ABSL_FLAG(int, size, 40, "Size of the problem in the wt file.");
|
ABSL_FLAG(int, size, 40, "Size of the problem in the wt file.");
|
||||||
|
|||||||
Reference in New Issue
Block a user