Make cvrptw_lib a header only lib.
- don't need extra stuff in Makefile anymore
This commit is contained in:
@@ -1,237 +0,0 @@
|
||||
// Copyright 2010-2018 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.
|
||||
|
||||
#include "examples/cpp/cvrptw_lib.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
int32 GetSeed(bool deterministic) {
|
||||
if (deterministic) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
}
|
||||
}
|
||||
|
||||
LocationContainer::LocationContainer(int64 speed, bool use_deterministic_seed)
|
||||
: randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) {
|
||||
CHECK_LT(0, speed_);
|
||||
}
|
||||
|
||||
void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max) {
|
||||
AddRandomLocation(x_max, y_max, 1);
|
||||
}
|
||||
|
||||
void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max,
|
||||
int duplicates) {
|
||||
const int64 x = randomizer_.Uniform(x_max + 1);
|
||||
const int64 y = randomizer_.Uniform(y_max + 1);
|
||||
for (int i = 0; i < duplicates; ++i) {
|
||||
AddLocation(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
int64 LocationContainer::ManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return locations_[from].DistanceTo(locations_[to]);
|
||||
}
|
||||
|
||||
int64 LocationContainer::NegManhattanDistance(
|
||||
RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
|
||||
return -ManhattanDistance(from, to);
|
||||
}
|
||||
|
||||
int64 LocationContainer::ManhattanTime(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return ManhattanDistance(from, to) / speed_;
|
||||
}
|
||||
|
||||
bool LocationContainer::SameLocation(RoutingModel::NodeIndex node1,
|
||||
RoutingModel::NodeIndex node2) const {
|
||||
if (node1 < locations_.size() && node2 < locations_.size()) {
|
||||
return locations_[node1].IsAtSameLocation(locations_[node2]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int64 LocationContainer::SameLocationFromIndex(int64 node1, int64 node2) const {
|
||||
// The direct conversion from constraint model indices to routing model
|
||||
// nodes is correct because the depot is node 0.
|
||||
// TODO(user): Fetch proper indices from routing model.
|
||||
return SameLocation(RoutingModel::NodeIndex(node1),
|
||||
RoutingModel::NodeIndex(node2));
|
||||
}
|
||||
|
||||
LocationContainer::Location::Location() : x_(0), y_(0) {}
|
||||
|
||||
LocationContainer::Location::Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
|
||||
int64 LocationContainer::Location::DistanceTo(const Location& location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
|
||||
bool LocationContainer::Location::IsAtSameLocation(
|
||||
const Location& location) const {
|
||||
return x_ == location.x_ && y_ == location.y_;
|
||||
}
|
||||
|
||||
int64 LocationContainer::Location::Abs(int64 value) {
|
||||
return std::max(value, -value);
|
||||
}
|
||||
|
||||
RandomDemand::RandomDemand(int size, RoutingModel::NodeIndex depot,
|
||||
bool use_deterministic_seed)
|
||||
: size_(size),
|
||||
depot_(depot),
|
||||
use_deterministic_seed_(use_deterministic_seed) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
|
||||
void RandomDemand::Initialize() {
|
||||
const int64 kDemandMax = 5;
|
||||
const int64 kDemandMin = 1;
|
||||
demand_.reset(new int64[size_]);
|
||||
MTRandom randomizer(GetSeed(use_deterministic_seed_));
|
||||
for (int order = 0; order < size_; ++order) {
|
||||
if (order == depot_) {
|
||||
demand_[order] = 0;
|
||||
} else {
|
||||
demand_[order] =
|
||||
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64 RandomDemand::Demand(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex /*to*/) const {
|
||||
return demand_[from.value()];
|
||||
}
|
||||
|
||||
ServiceTimePlusTransition::ServiceTimePlusTransition(
|
||||
int64 time_per_demand_unit, RoutingModel::NodeEvaluator2* demand,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: time_per_demand_unit_(time_per_demand_unit),
|
||||
demand_(demand),
|
||||
transition_time_(transition_time) {}
|
||||
|
||||
int64 ServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return time_per_demand_unit_ * demand_->Run(from, to) +
|
||||
transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
StopServiceTimePlusTransition::StopServiceTimePlusTransition(
|
||||
int64 stop_time, const LocationContainer& location_container,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: stop_time_(stop_time),
|
||||
location_container_(location_container),
|
||||
transition_time_(transition_time) {}
|
||||
|
||||
int64 StopServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return location_container_.SameLocation(from, to)
|
||||
? 0
|
||||
: stop_time_ + transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
void DisplayPlan(
|
||||
const RoutingModel& routing, const operations_research::Assignment& plan,
|
||||
bool use_same_vehicle_costs, int64 max_nodes_per_group,
|
||||
int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int64 order = 0; order < routing.Size(); ++order) {
|
||||
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", routing.IndexToNode(order).value());
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", routing.IndexToNode(order).value());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dropped.empty()) {
|
||||
plan_output += "Dropped orders:" + dropped + "\n";
|
||||
}
|
||||
|
||||
if (use_same_vehicle_costs) {
|
||||
int group_size = 0;
|
||||
int64 group_same_vehicle_cost = 0;
|
||||
std::set<int> visited;
|
||||
for (int64 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 order = routing.Start(route_number);
|
||||
StringAppendF(&plan_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
operations_research::IntVar* const load_var =
|
||||
capacity_dimension.CumulVar(order);
|
||||
operations_research::IntVar* const time_var =
|
||||
time_dimension.CumulVar(order);
|
||||
operations_research::IntVar* const slack_var =
|
||||
routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order);
|
||||
if (slack_var != nullptr && plan.Contains(slack_var)) {
|
||||
StringAppendF(&plan_output,
|
||||
"%lld Load(%lld) Time(%lld, %lld) Slack(%lld, %lld)",
|
||||
routing.IndexToNode(order).value(),
|
||||
plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var), plan.Min(slack_var),
|
||||
plan.Max(slack_var));
|
||||
} else {
|
||||
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld)",
|
||||
routing.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
|
||||
@@ -17,8 +17,11 @@
|
||||
#define OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -127,6 +130,218 @@ void DisplayPlan(
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension);
|
||||
|
||||
int32 GetSeed(bool deterministic) {
|
||||
if (deterministic) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
}
|
||||
}
|
||||
|
||||
LocationContainer::LocationContainer(int64 speed, bool use_deterministic_seed)
|
||||
: randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) {
|
||||
CHECK_LT(0, speed_);
|
||||
}
|
||||
|
||||
void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max) {
|
||||
AddRandomLocation(x_max, y_max, 1);
|
||||
}
|
||||
|
||||
void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max,
|
||||
int duplicates) {
|
||||
const int64 x = randomizer_.Uniform(x_max + 1);
|
||||
const int64 y = randomizer_.Uniform(y_max + 1);
|
||||
for (int i = 0; i < duplicates; ++i) {
|
||||
AddLocation(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
int64 LocationContainer::ManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return locations_[from].DistanceTo(locations_[to]);
|
||||
}
|
||||
|
||||
int64 LocationContainer::NegManhattanDistance(
|
||||
RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
|
||||
return -ManhattanDistance(from, to);
|
||||
}
|
||||
|
||||
int64 LocationContainer::ManhattanTime(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return ManhattanDistance(from, to) / speed_;
|
||||
}
|
||||
|
||||
bool LocationContainer::SameLocation(RoutingModel::NodeIndex node1,
|
||||
RoutingModel::NodeIndex node2) const {
|
||||
if (node1 < locations_.size() && node2 < locations_.size()) {
|
||||
return locations_[node1].IsAtSameLocation(locations_[node2]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int64 LocationContainer::SameLocationFromIndex(int64 node1, int64 node2) const {
|
||||
// The direct conversion from constraint model indices to routing model
|
||||
// nodes is correct because the depot is node 0.
|
||||
// TODO(user): Fetch proper indices from routing model.
|
||||
return SameLocation(RoutingModel::NodeIndex(node1),
|
||||
RoutingModel::NodeIndex(node2));
|
||||
}
|
||||
|
||||
LocationContainer::Location::Location() : x_(0), y_(0) {}
|
||||
|
||||
LocationContainer::Location::Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
|
||||
int64 LocationContainer::Location::DistanceTo(const Location& location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
|
||||
bool LocationContainer::Location::IsAtSameLocation(
|
||||
const Location& location) const {
|
||||
return x_ == location.x_ && y_ == location.y_;
|
||||
}
|
||||
|
||||
int64 LocationContainer::Location::Abs(int64 value) {
|
||||
return std::max(value, -value);
|
||||
}
|
||||
|
||||
RandomDemand::RandomDemand(int size, RoutingModel::NodeIndex depot,
|
||||
bool use_deterministic_seed)
|
||||
: size_(size),
|
||||
depot_(depot),
|
||||
use_deterministic_seed_(use_deterministic_seed) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
|
||||
void RandomDemand::Initialize() {
|
||||
const int64 kDemandMax = 5;
|
||||
const int64 kDemandMin = 1;
|
||||
demand_.reset(new int64[size_]);
|
||||
MTRandom randomizer(GetSeed(use_deterministic_seed_));
|
||||
for (int order = 0; order < size_; ++order) {
|
||||
if (order == depot_) {
|
||||
demand_[order] = 0;
|
||||
} else {
|
||||
demand_[order] =
|
||||
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64 RandomDemand::Demand(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex /*to*/) const {
|
||||
return demand_[from.value()];
|
||||
}
|
||||
|
||||
ServiceTimePlusTransition::ServiceTimePlusTransition(
|
||||
int64 time_per_demand_unit, RoutingModel::NodeEvaluator2* demand,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: time_per_demand_unit_(time_per_demand_unit),
|
||||
demand_(demand),
|
||||
transition_time_(transition_time) {}
|
||||
|
||||
int64 ServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return time_per_demand_unit_ * demand_->Run(from, to) +
|
||||
transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
StopServiceTimePlusTransition::StopServiceTimePlusTransition(
|
||||
int64 stop_time, const LocationContainer& location_container,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: stop_time_(stop_time),
|
||||
location_container_(location_container),
|
||||
transition_time_(transition_time) {}
|
||||
|
||||
int64 StopServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return location_container_.SameLocation(from, to)
|
||||
? 0
|
||||
: stop_time_ + transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
void DisplayPlan(
|
||||
const RoutingModel& routing, const operations_research::Assignment& plan,
|
||||
bool use_same_vehicle_costs, int64 max_nodes_per_group,
|
||||
int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int64 order = 0; order < routing.Size(); ++order) {
|
||||
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", routing.IndexToNode(order).value());
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", routing.IndexToNode(order).value());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dropped.empty()) {
|
||||
plan_output += "Dropped orders:" + dropped + "\n";
|
||||
}
|
||||
|
||||
if (use_same_vehicle_costs) {
|
||||
int group_size = 0;
|
||||
int64 group_same_vehicle_cost = 0;
|
||||
std::set<int> visited;
|
||||
for (int64 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 order = routing.Start(route_number);
|
||||
StringAppendF(&plan_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
operations_research::IntVar* const load_var =
|
||||
capacity_dimension.CumulVar(order);
|
||||
operations_research::IntVar* const time_var =
|
||||
time_dimension.CumulVar(order);
|
||||
operations_research::IntVar* const slack_var =
|
||||
routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order);
|
||||
if (slack_var != nullptr && plan.Contains(slack_var)) {
|
||||
StringAppendF(&plan_output,
|
||||
"%lld Load(%lld) Time(%lld, %lld) Slack(%lld, %lld)",
|
||||
routing.IndexToNode(order).value(),
|
||||
plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var), plan.Min(slack_var),
|
||||
plan.Max(slack_var));
|
||||
} else {
|
||||
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld)",
|
||||
routing.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_
|
||||
|
||||
Reference in New Issue
Block a user