Files
ortools-clone/examples/cpp/cvrptw_lib.h

349 lines
12 KiB
C
Raw Normal View History

2021-04-02 10:08:51 +02:00
// Copyright 2010-2021 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_
2021-08-23 14:46:12 +02:00
#include <cstdint>
#include <memory>
#include <set>
2018-11-28 10:37:45 +01:00
#include "absl/strings/str_format.h"
#include "ortools/base/logging.h"
2018-06-08 16:40:43 +02:00
#include "ortools/constraint_solver/routing.h"
#include "ortools/util/random_engine.h"
namespace operations_research {
2021-04-02 14:58:16 +02:00
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 {
2020-10-22 23:36:58 +02:00
public:
2021-04-02 14:58:16 +02:00
LocationContainer(int64_t speed, bool use_deterministic_seed);
2021-08-23 14:46:12 +02:00
void AddLocation(int64_t x, int64_t y) {
locations_.push_back(Location(x, y));
}
2021-04-02 14:58:16 +02:00
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,
2021-08-23 14:46:12 +02:00
RoutingIndexManager::NodeIndex to) const;
2021-04-02 14:58:16 +02:00
int64_t NegManhattanDistance(RoutingIndexManager::NodeIndex from,
2021-08-23 14:46:12 +02:00
RoutingIndexManager::NodeIndex to) const;
2021-04-02 14:58:16 +02:00
int64_t ManhattanTime(RoutingIndexManager::NodeIndex from,
2021-08-23 14:46:12 +02:00
RoutingIndexManager::NodeIndex to) const;
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
bool SameLocation(RoutingIndexManager::NodeIndex node1,
RoutingIndexManager::NodeIndex node2) const;
2021-04-02 14:58:16 +02:00
int64_t SameLocationFromIndex(int64_t node1, int64_t node2) const;
2020-10-22 23:36:58 +02:00
private:
class Location {
2020-10-22 23:36:58 +02:00
public:
Location();
2021-04-02 14:58:16 +02:00
Location(int64_t x, int64_t y);
int64_t DistanceTo(const Location& location) const;
2020-10-29 14:25:39 +01:00
bool IsAtSameLocation(const Location& location) const;
2020-10-22 23:36:58 +02:00
private:
2021-04-02 14:58:16 +02:00
static int64_t Abs(int64_t value);
2021-04-02 14:58:16 +02:00
int64_t x_;
int64_t y_;
};
random_engine_t randomizer_;
2021-04-02 14:58:16 +02:00
const int64_t speed_;
2020-11-19 00:23:00 +01:00
absl::StrongVector<RoutingIndexManager::NodeIndex, Location> locations_;
};
// Random demand.
class RandomDemand {
2020-10-22 23:36:58 +02:00
public:
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
RandomDemand(int size, RoutingIndexManager::NodeIndex depot,
bool use_deterministic_seed);
void Initialize();
2021-04-02 14:58:16 +02:00
int64_t Demand(RoutingIndexManager::NodeIndex from,
2021-08-23 14:46:12 +02:00
RoutingIndexManager::NodeIndex to) const;
2020-10-22 23:36:58 +02:00
private:
2021-04-02 14:58:16 +02:00
std::unique_ptr<int64_t[]> demand_;
const int size_;
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
const RoutingIndexManager::NodeIndex depot_;
const bool use_deterministic_seed_;
};
// Service time (proportional to demand) + transition time callback.
class ServiceTimePlusTransition {
2020-10-22 23:36:58 +02:00
public:
2021-08-23 14:46:12 +02:00
ServiceTimePlusTransition(
int64_t time_per_demand_unit,
operations_research::RoutingNodeEvaluator2 demand,
operations_research::RoutingNodeEvaluator2 transition_time);
2021-04-02 14:58:16 +02:00
int64_t Compute(RoutingIndexManager::NodeIndex from,
2021-08-23 14:46:12 +02:00
RoutingIndexManager::NodeIndex to) const;
2020-10-22 23:36:58 +02:00
private:
2021-04-02 14:58:16 +02:00
const int64_t time_per_demand_unit_;
2021-08-23 14:46:12 +02:00
operations_research::RoutingNodeEvaluator2 demand_;
operations_research::RoutingNodeEvaluator2 transition_time_;
};
// Stop service time + transition time callback.
class StopServiceTimePlusTransition {
2020-10-22 23:36:58 +02:00
public:
2021-08-23 14:46:12 +02:00
StopServiceTimePlusTransition(
int64_t stop_time, const LocationContainer& location_container,
operations_research::RoutingNodeEvaluator2 transition_time);
2021-04-02 14:58:16 +02:00
int64_t Compute(RoutingIndexManager::NodeIndex from,
2021-08-23 14:46:12 +02:00
RoutingIndexManager::NodeIndex to) const;
2020-10-22 23:36:58 +02:00
private:
2021-04-02 14:58:16 +02:00
const int64_t stop_time_;
2020-10-29 14:25:39 +01:00
const LocationContainer& location_container_;
2021-08-23 14:46:12 +02:00
operations_research::RoutingNodeEvaluator2 demand_;
operations_research::RoutingNodeEvaluator2 transition_time_;
};
// Route plan displayer.
// TODO(user): Move the display code to the routing library.
2020-10-22 23:36:58 +02:00
void DisplayPlan(
2020-10-29 14:25:39 +01:00
const operations_research::RoutingIndexManager& manager,
const operations_research::RoutingModel& routing,
const operations_research::Assignment& plan, bool use_same_vehicle_costs,
2021-04-02 14:58:16 +02:00
int64_t max_nodes_per_group, int64_t same_vehicle_cost,
2020-10-29 14:25:39 +01:00
const operations_research::RoutingDimension& capacity_dimension,
const operations_research::RoutingDimension& time_dimension);
2018-11-28 11:15:45 +01:00
using NodeIndex = RoutingIndexManager::NodeIndex;
int32_t GetSeed(bool deterministic) {
if (deterministic) {
return 0;
} else {
return std::random_device()();
}
}
2021-04-02 14:58:16 +02:00
LocationContainer::LocationContainer(int64_t speed, bool use_deterministic_seed)
: randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) {
CHECK_LT(0, speed_);
}
2021-04-02 14:58:16 +02:00
void LocationContainer::AddRandomLocation(int64_t x_max, int64_t y_max) {
AddRandomLocation(x_max, y_max, 1);
}
2021-04-02 14:58:16 +02:00
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);
}
}
2022-02-16 16:19:26 +01:00
int64_t LocationContainer::ManhattanDistance(NodeIndex from,
NodeIndex to) const {
return locations_[from].DistanceTo(locations_[to]);
}
2021-04-02 14:58:16 +02:00
int64_t LocationContainer::NegManhattanDistance(NodeIndex from,
2022-02-16 16:19:26 +01:00
NodeIndex to) const {
return -ManhattanDistance(from, to);
}
2021-04-02 14:58:16 +02:00
int64_t LocationContainer::ManhattanTime(NodeIndex from, NodeIndex to) const {
return ManhattanDistance(from, to) / speed_;
}
2018-11-28 11:15:45 +01:00
bool LocationContainer::SameLocation(NodeIndex node1, NodeIndex node2) const {
if (node1 < locations_.size() && node2 < locations_.size()) {
return locations_[node1].IsAtSameLocation(locations_[node2]);
}
return false;
}
2022-02-16 16:19:26 +01:00
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.
2018-11-28 11:15:45 +01:00
return SameLocation(NodeIndex(node1), NodeIndex(node2));
}
LocationContainer::Location::Location() : x_(0), y_(0) {}
2021-04-02 14:58:16 +02:00
LocationContainer::Location::Location(int64_t x, int64_t y) : x_(x), y_(y) {}
2022-02-16 16:19:26 +01:00
int64_t LocationContainer::Location::DistanceTo(
const Location& location) const {
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
}
2020-10-22 23:36:58 +02:00
bool LocationContainer::Location::IsAtSameLocation(
2020-10-29 14:25:39 +01:00
const Location& location) const {
return x_ == location.x_ && y_ == location.y_;
}
2021-04-02 14:58:16 +02:00
int64_t LocationContainer::Location::Abs(int64_t value) {
return std::max(value, -value);
}
2018-11-28 11:15:45 +01:00
RandomDemand::RandomDemand(int size, NodeIndex depot,
bool use_deterministic_seed)
2020-10-22 23:36:58 +02:00
: size_(size),
depot_(depot),
use_deterministic_seed_(use_deterministic_seed) {
CHECK_LT(0, size_);
}
void RandomDemand::Initialize() {
2021-04-02 14:58:16 +02:00
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 {
2022-02-16 16:19:26 +01:00
demand_[order] = kDemandMin + absl::Uniform(randomizer, 0,
kDemandMax - kDemandMin + 1);
}
}
}
2021-04-02 14:58:16 +02:00
int64_t RandomDemand::Demand(NodeIndex from, NodeIndex /*to*/) const {
return demand_[from.value()];
}
ServiceTimePlusTransition::ServiceTimePlusTransition(
2021-04-02 14:58:16 +02:00
int64_t time_per_demand_unit, RoutingNodeEvaluator2 demand,
2018-11-28 11:15:45 +01:00
RoutingNodeEvaluator2 transition_time)
2020-10-22 23:36:58 +02:00
: time_per_demand_unit_(time_per_demand_unit),
demand_(std::move(demand)),
2018-11-28 11:15:45 +01:00
transition_time_(std::move(transition_time)) {}
2021-04-02 14:58:16 +02:00
int64_t ServiceTimePlusTransition::Compute(NodeIndex from, NodeIndex to) const {
2018-11-28 11:15:45 +01:00
return time_per_demand_unit_ * demand_(from, to) + transition_time_(from, to);
}
StopServiceTimePlusTransition::StopServiceTimePlusTransition(
2021-04-02 14:58:16 +02:00
int64_t stop_time, const LocationContainer& location_container,
2018-11-28 11:15:45 +01:00
RoutingNodeEvaluator2 transition_time)
2020-10-22 23:36:58 +02:00
: stop_time_(stop_time),
location_container_(location_container),
2018-11-28 11:15:45 +01:00
transition_time_(std::move(transition_time)) {}
2021-04-02 14:58:16 +02:00
int64_t StopServiceTimePlusTransition::Compute(NodeIndex from,
2022-02-16 16:19:26 +01:00
NodeIndex to) const {
return location_container_.SameLocation(from, to)
? 0
2018-11-28 11:15:45 +01:00
: stop_time_ + transition_time_(from, to);
}
2020-10-22 23:36:58 +02:00
void DisplayPlan(
2020-10-29 14:25:39 +01:00
const RoutingIndexManager& manager, const RoutingModel& routing,
const operations_research::Assignment& plan, bool use_same_vehicle_costs,
2021-04-02 14:58:16 +02:00
int64_t max_nodes_per_group, int64_t same_vehicle_cost,
2020-10-29 14:25:39 +01:00
const operations_research::RoutingDimension& capacity_dimension,
const operations_research::RoutingDimension& time_dimension) {
// Display plan cost.
2018-11-28 10:37:45 +01:00
std::string plan_output = absl::StrFormat("Cost %d\n", plan.ObjectiveValue());
// Display dropped orders.
std::string dropped;
2021-04-02 14:58:16 +02:00
for (int64_t order = 0; order < routing.Size(); ++order) {
2020-10-22 23:36:58 +02:00
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
if (plan.Value(routing.NextVar(order)) == order) {
if (dropped.empty()) {
2018-11-28 11:15:45 +01:00
absl::StrAppendFormat(&dropped, " %d",
manager.IndexToNode(order).value());
} else {
2018-11-28 11:15:45 +01:00
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;
2021-04-02 14:58:16 +02:00
int64_t group_same_vehicle_cost = 0;
std::set<int> visited;
2021-04-02 14:58:16 +02:00
for (int64_t order = 0; order < routing.Size(); ++order) {
2020-10-22 23:36:58 +02:00
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) {
2021-04-02 14:58:16 +02:00
int64_t order = routing.Start(route_number);
2018-11-28 11:15:45 +01:00
absl::StrAppendFormat(&plan_output, "Route %d: ", route_number);
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
plan_output += "Empty\n";
} else {
while (true) {
2020-10-29 14:25:39 +01:00
operations_research::IntVar* const load_var =
capacity_dimension.CumulVar(order);
2020-10-29 14:25:39 +01:00
operations_research::IntVar* const time_var =
time_dimension.CumulVar(order);
2020-10-29 14:25:39 +01:00
operations_research::IntVar* const slack_var =
routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order);
if (slack_var != nullptr && plan.Contains(slack_var)) {
2018-11-28 11:15:45 +01:00
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 {
2018-11-28 11:15:45 +01:00
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));
}
2020-10-22 23:36:58 +02:00
if (routing.IsEnd(order)) break;
plan_output += " -> ";
order = plan.Value(routing.NextVar(order));
}
plan_output += "\n";
}
}
LOG(INFO) << plan_output;
}
2020-10-22 23:36:58 +02:00
} // namespace operations_research
2020-10-22 23:36:58 +02:00
#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_