ported examples

This commit is contained in:
lperron@google.com
2014-07-09 11:09:30 +00:00
parent f7c2c5d8e5
commit a2f8554a6d
50 changed files with 1050 additions and 62 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -0,0 +1,315 @@
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 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 "base/unique_ptr.h"
#include <vector>
#include "base/callback.h"
#include "base/commandlineflags.h"
#include "base/commandlineflags.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "constraint_solver/routing.h"
#include "base/random.h"
using operations_research::Assignment;
using operations_research::IntVar;
using operations_research::RoutingDimension;
using operations_research::RoutingModel;
using operations_research::Solver;
using operations_research::ACMRandom;
using operations_research::StringAppendF;
using operations_research::StringPrintf;
using operations_research::scoped_ptr;
DECLARE_string(routing_first_solution);
DECLARE_bool(routing_no_lns);
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
DEFINE_bool(vrp_use_deterministic_random_seed, false,
"Use deterministic random seeds.");
const char* kTime = "Time";
const char* kCapacity = "Capacity";
const char* kFuel = "Fuel";
// Random seed generator.
int32 GetSeed() {
if (FLAGS_vrp_use_deterministic_random_seed) {
return ACMRandom::DeterministicSeed();
} else {
return ACMRandom::HostnamePidTimeSeed();
}
}
// Returns true if node is a refueling node (based on node / refuel node ratio).
bool IsRefuelNode(int64 node) {
const int64 kRefuelNodeRatio = 10;
return (node % kRefuelNodeRatio == 0);
}
// Location container, contains positions of orders and can be used to obtain
// Manhattan distances/times between locations.
class LocationContainer {
public:
explicit LocationContainer(int64 speed)
: randomizer_(GetSeed()), speed_(speed) {
CHECK_LT(0, speed_);
}
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
void AddRandomLocation(int64 x_max, int64 y_max) {
AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1));
}
int64 ManhattanDistance(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return locations_[from].DistanceTo(locations_[to]);
}
int64 NegManhattanDistance(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return -ManhattanDistance(from, to);
}
int64 ManhattanTime(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return ManhattanDistance(from, to) / speed_;
}
private:
class Location {
public:
Location() : x_(0), y_(0) {}
Location(int64 x, int64 y) : x_(x), y_(y) {}
int64 DistanceTo(const Location& location) const {
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
}
private:
static int64 Abs(int64 value) { return std::max(value, -value); }
int64 x_;
int64 y_;
};
ACMRandom randomizer_;
const int64 speed_;
ITIVector<RoutingModel::NodeIndex, Location> locations_;
};
// Random demand.
class RandomDemand {
public:
RandomDemand(int size, RoutingModel::NodeIndex depot)
: size_(size), depot_(depot) {
CHECK_LT(0, size_);
}
void Initialize() {
const int64 kDemandMax = 5;
const int64 kDemandMin = 1;
demand_.reset(new int64[size_]);
ACMRandom randomizer(GetSeed());
for (int order = 0; order < size_; ++order) {
if (order == depot_) {
demand_[order] = 0;
} else {
demand_[order] =
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
}
}
}
int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
// Refuel nodes don't have a demand.
if (!IsRefuelNode(from.value())) {
return demand_[from.value()];
}
return 0;
}
private:
std::unique_ptr<int64[]> demand_;
const int size_;
const RoutingModel::NodeIndex depot_;
};
// Service time (proportional to demand) + transition time callback.
class ServiceTimePlusTransition {
public:
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 Compute(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return time_per_demand_unit_ * demand_->Run(from, to) +
transition_time_->Run(from, to);
}
private:
const int64 time_per_demand_unit_;
std::unique_ptr<RoutingModel::NodeEvaluator2> demand_;
std::unique_ptr<RoutingModel::NodeEvaluator2> transition_time_;
};
// Route plan displayer.
// TODO(user): Move the display code to the routing library.
void DisplayPlan(const RoutingModel& routing, const Assignment& plan) {
// Display plan cost.
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
// Display dropped orders.
std::string dropped;
for (int order = 1; order < routing.nodes(); ++order) {
if (plan.Value(routing.NextVar(order)) == order) {
if (dropped.empty()) {
StringAppendF(&dropped, " %d", order);
} else {
StringAppendF(&dropped, ", %d", order);
}
}
}
if (!dropped.empty()) {
plan_output += "Dropped orders:" + dropped + "\n";
}
// Display actual output for each vehicle.
const RoutingDimension& capacity_dimension =
routing.GetDimensionOrDie(kCapacity);
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel);
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) {
IntVar* const load_var = capacity_dimension.CumulVar(order);
IntVar* const time_var = time_dimension.CumulVar(order);
IntVar* const fuel_var = fuel_dimension.CumulVar(order);
StringAppendF(&plan_output,
"%lld Load(%lld) Time(%lld, %lld) Fuel(%lld, %lld) -> ",
order, plan.Value(load_var), plan.Min(time_var),
plan.Max(time_var), plan.Min(fuel_var),
plan.Max(fuel_var));
if (routing.IsEnd(order)) break;
order = plan.Value(routing.NextVar(order));
}
}
plan_output += "\n";
}
LOG(INFO) << plan_output;
}
int main(int argc, char** argv) {
google::ParseCommandLineFlags( &argc, &argv, true);
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
// VRP of size FLAGS_vrp_size.
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
// the routes are at node 0.
const RoutingModel::NodeIndex kDepot(0);
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles);
routing.SetDepot(kDepot);
// Setting first solution heuristic (cheapest addition).
FLAGS_routing_first_solution = "PathCheapestArc";
// Disabling Large Neighborhood Search, comment out to activate it.
FLAGS_routing_no_lns = true;
// Setting up locations.
const int64 kXMax = 100000;
const int64 kYMax = 100000;
const int64 kSpeed = 10;
LocationContainer locations(kSpeed);
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
locations.AddRandomLocation(kXMax, kYMax);
}
// Setting the cost function.
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
// Adding capacity dimension constraints.
const int64 kVehicleCapacity = 40;
const int64 kNullCapacitySlack = 0;
RandomDemand demand(routing.nodes(), kDepot);
demand.Initialize();
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
kNullCapacitySlack, kVehicleCapacity,
/*fix_start_cumul_to_zero=*/true, kCapacity);
// Adding time dimension constraints.
const int64 kTimePerDemandUnit = 300;
const int64 kHorizon = 24 * 3600;
ServiceTimePlusTransition time(
kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand),
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
routing.AddDimension(
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
// Adding time windows.
ACMRandom randomizer(GetSeed());
const int64 kTWDuration = 5 * 3600;
for (int order = 1; order < routing.nodes(); ++order) {
if (!IsRefuelNode(order)) {
const int64 start = randomizer.Uniform(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 kFuelCapacity = kXMax + kYMax;
routing.AddDimension(
NewPermanentCallback(&locations,
&LocationContainer::NegManhattanDistance),
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 kPenalty = 100000;
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
order < routing.nodes(); ++order) {
std::vector<RoutingModel::NodeIndex> orders(1, order);
routing.AddDisjunction(orders, kPenalty);
}
// Solve, returns a solution if any (owned by RoutingModel).
const Assignment* solution = routing.Solve();
if (solution != NULL) {
DisplayPlan(routing, *solution);
} else {
LOG(INFO) << "No solution found.";
}
return 0;
}

View File

@@ -0,0 +1,301 @@
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 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 "base/unique_ptr.h"
#include <vector>
#include "base/callback.h"
#include "base/commandlineflags.h"
#include "base/commandlineflags.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "constraint_solver/routing.h"
#include "base/random.h"
using operations_research::Assignment;
using operations_research::IntervalVar;
using operations_research::IntVar;
using operations_research::RoutingDimension;
using operations_research::RoutingModel;
using operations_research::Solver;
using operations_research::ACMRandom;
using operations_research::StringAppendF;
using operations_research::StringPrintf;
using operations_research::scoped_ptr;
DECLARE_string(routing_first_solution);
DECLARE_bool(routing_no_lns);
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
DEFINE_bool(vrp_use_deterministic_random_seed, false,
"Use deterministic random seeds.");
const char* kTime = "Time";
const char* kCapacity = "Capacity";
// Random seed generator.
int32 GetSeed() {
if (FLAGS_vrp_use_deterministic_random_seed) {
return ACMRandom::DeterministicSeed();
} else {
return ACMRandom::HostnamePidTimeSeed();
}
}
// Location container, contains positions of orders and can be used to obtain
// Manhattan distances/times between locations.
class LocationContainer {
public:
explicit LocationContainer(int64 speed)
: randomizer_(GetSeed()), speed_(speed) {
CHECK_LT(0, speed_);
}
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
void AddRandomLocation(int64 x_max, int64 y_max) {
AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1));
}
int64 ManhattanDistance(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return locations_[from].DistanceTo(locations_[to]);
}
int64 ManhattanTime(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return ManhattanDistance(from, to) / speed_;
}
private:
class Location {
public:
Location() : x_(0), y_(0) {}
Location(int64 x, int64 y) : x_(x), y_(y) {}
int64 DistanceTo(const Location& location) const {
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
}
private:
static int64 Abs(int64 value) { return std::max(value, -value); }
int64 x_;
int64 y_;
};
ACMRandom randomizer_;
const int64 speed_;
ITIVector<RoutingModel::NodeIndex, Location> locations_;
};
// Random demand.
class RandomDemand {
public:
RandomDemand(int size, RoutingModel::NodeIndex depot)
: size_(size), depot_(depot) {
CHECK_LT(0, size_);
}
void Initialize() {
const int64 kDemandMax = 5;
const int64 kDemandMin = 1;
demand_.reset(new int64[size_]);
ACMRandom randomizer(GetSeed());
for (int order = 0; order < size_; ++order) {
if (order == depot_) {
demand_[order] = 0;
} else {
demand_[order] =
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
}
}
}
int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
return demand_[from.value()];
}
private:
std::unique_ptr<int64[]> demand_;
const int size_;
const RoutingModel::NodeIndex depot_;
};
// Service time (proportional to demand) + transition time callback.
class ServiceTimePlusTransition {
public:
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 Compute(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return time_per_demand_unit_ * demand_->Run(from, to) +
transition_time_->Run(from, to);
}
private:
const int64 time_per_demand_unit_;
std::unique_ptr<RoutingModel::NodeEvaluator2> demand_;
std::unique_ptr<RoutingModel::NodeEvaluator2> transition_time_;
};
// Route plan displayer.
// TODO(user): Move the display code to the routing library.
void DisplayPlan(const RoutingModel& routing, const Assignment& plan) {
// Display plan cost.
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
// Display dropped orders.
std::string dropped;
for (int order = 1; order < routing.nodes(); ++order) {
if (plan.Value(routing.NextVar(order)) == order) {
if (dropped.empty()) {
StringAppendF(&dropped, " %d", order);
} else {
StringAppendF(&dropped, ", %d", order);
}
}
}
if (!dropped.empty()) {
plan_output += "Dropped orders:" + dropped + "\n";
}
// Display actual output for each vehicle.
const RoutingDimension& capacity_dimension =
routing.GetDimensionOrDie(kCapacity);
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
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) {
IntVar* const load_var = capacity_dimension.CumulVar(order);
IntVar* const time_var = time_dimension.CumulVar(order);
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld) -> ",
order, plan.Value(load_var), plan.Min(time_var),
plan.Max(time_var));
if (routing.IsEnd(order)) break;
order = plan.Value(routing.NextVar(order));
}
}
}
LOG(INFO) << plan_output;
}
int main(int argc, char** argv) {
google::ParseCommandLineFlags( &argc, &argv, true);
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
// VRP of size FLAGS_vrp_size.
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
// the routes are at node 0.
const RoutingModel::NodeIndex kDepot(0);
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles);
routing.SetDepot(kDepot);
// Setting first solution heuristic (cheapest addition).
FLAGS_routing_first_solution = "PathCheapestArc";
// Disabling Large Neighborhood Search, comment out to activate it.
FLAGS_routing_no_lns = true;
// Setting up locations.
const int64 kXMax = 100000;
const int64 kYMax = 100000;
const int64 kSpeed = 10;
LocationContainer locations(kSpeed);
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
locations.AddRandomLocation(kXMax, kYMax);
}
// Setting the cost function.
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
// Adding capacity dimension constraints.
const int64 kVehicleCapacity = 40;
const int64 kNullCapacitySlack = 0;
RandomDemand demand(routing.nodes(), kDepot);
demand.Initialize();
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
kNullCapacitySlack, kVehicleCapacity,
/*fix_start_cumul_to_zero=*/true, kCapacity);
// Adding time dimension constraints.
const int64 kTimePerDemandUnit = 300;
const int64 kHorizon = 24 * 3600;
ServiceTimePlusTransition time(
kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand),
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
routing.AddDimension(
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
// Adding time windows.
ACMRandom randomizer(GetSeed());
const int64 kTWDuration = 5 * 3600;
for (int order = 1; order < routing.nodes(); ++order) {
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
routing.CumulVar(order, kTime)->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 < FLAGS_vrp_vehicles; ++i) {
start_end_times.push_back(routing.CumulVar(routing.End(i), kTime));
start_end_times.push_back(routing.CumulVar(routing.Start(i), kTime));
}
// Build corresponding time intervals.
const int64 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 kDepotCapacity = 5;
std::vector<int64> 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 kPenalty = 100000;
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
order < routing.nodes(); ++order) {
std::vector<RoutingModel::NodeIndex> orders(1, order);
routing.AddDisjunction(orders, kPenalty);
}
// Solve, returns a solution if any (owned by RoutingModel).
const Assignment* solution = routing.Solve();
if (solution != NULL) {
DisplayPlan(routing, *solution);
} else {
LOG(INFO) << "No solution found.";
}
return 0;
}

View File

@@ -0,0 +1,367 @@
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 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 "base/unique_ptr.h"
#include <vector>
#include "base/callback.h"
#include "base/commandlineflags.h"
#include "base/commandlineflags.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "base/join.h"
#include "constraint_solver/routing.h"
#include "base/random.h"
using operations_research::Assignment;
using operations_research::IntervalVar;
using operations_research::IntExpr;
using operations_research::IntVar;
using operations_research::RoutingDimension;
using operations_research::RoutingModel;
using operations_research::Solver;
using operations_research::ACMRandom;
using operations_research::StrCat;
using operations_research::StringAppendF;
using operations_research::StringPrintf;
using operations_research::scoped_ptr;
DECLARE_string(routing_first_solution);
DECLARE_bool(routing_no_lns);
DEFINE_int32(vrp_stops, 25, "Stop locations in the problem.");
DEFINE_int32(vrp_orders_per_stop, 5, "Nodes for each stop.");
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
DEFINE_bool(vrp_use_deterministic_random_seed, false,
"Use deterministic random seeds.");
const char* kTime = "Time";
const char* kCapacity = "Capacity";
// Random seed generator.
int32 GetSeed() {
if (FLAGS_vrp_use_deterministic_random_seed) {
return ACMRandom::DeterministicSeed();
} else {
return ACMRandom::HostnamePidTimeSeed();
}
}
// Location container, contains positions of orders and can be used to obtain
// Manhattan distances/times between locations.
class LocationContainer {
public:
explicit LocationContainer(int64 speed)
: randomizer_(GetSeed()), speed_(speed) {
CHECK_LT(0, speed_);
}
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
void 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 ManhattanDistance(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return locations_[from].DistanceTo(locations_[to]);
}
int64 ManhattanTime(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return ManhattanDistance(from, to) / speed_;
}
bool SameLocation(RoutingModel::NodeIndex node1,
RoutingModel::NodeIndex node2) const {
if (node1 < locations_.size() && node2 < locations_.size()) {
return locations_[node1].IsAtSameLocation(locations_[node2]);
}
return false;
}
int64 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));
}
private:
class Location {
public:
Location() : x_(0), y_(0) {}
Location(int64 x, int64 y) : x_(x), y_(y) {}
int64 DistanceTo(const Location& location) const {
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
}
bool IsAtSameLocation(const Location& location) const {
return x_ == location.x_ && y_ == location.y_;
}
private:
static int64 Abs(int64 value) { return std::max(value, -value); }
int64 x_;
int64 y_;
};
ACMRandom randomizer_;
const int64 speed_;
ITIVector<RoutingModel::NodeIndex, Location> locations_;
};
// Random demand.
class RandomDemand {
public:
RandomDemand(int size, RoutingModel::NodeIndex depot)
: size_(size), depot_(depot) {
CHECK_LT(0, size_);
}
void Initialize() {
const int64 kDemandMax = 5;
const int64 kDemandMin = 1;
demand_.reset(new int64[size_]);
ACMRandom randomizer(GetSeed());
for (int order = 0; order < size_; ++order) {
if (order == depot_) {
demand_[order] = 0;
} else {
demand_[order] =
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
}
}
}
int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
return demand_[from.value()];
}
private:
std::unique_ptr<int64[]> demand_;
const int size_;
const RoutingModel::NodeIndex depot_;
};
// Stop service time + transition time callback.
class StopServiceTimePlusTransition {
public:
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 Compute(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return location_container_.SameLocation(from, to) ? 0
: stop_time_ + transition_time_->Run(from, to);
}
private:
const int64 stop_time_;
const LocationContainer& location_container_;
std::unique_ptr<RoutingModel::NodeEvaluator2> demand_;
std::unique_ptr<RoutingModel::NodeEvaluator2> transition_time_;
};
// Route plan displayer.
// TODO(user): Move the display code to the routing library.
void DisplayPlan(const RoutingModel& routing, const Assignment& plan) {
// Display plan cost.
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
// Display dropped orders.
std::string dropped;
for (int order = 1; order < routing.nodes(); ++order) {
if (plan.Value(routing.NextVar(order)) == order) {
if (dropped.empty()) {
StringAppendF(&dropped, " %d", order);
} else {
StringAppendF(&dropped, ", %d", order);
}
}
}
if (!dropped.empty()) {
plan_output += "Dropped orders:" + dropped + "\n";
}
LOG(INFO) << plan_output;
// Display actual output for each vehicle.
const RoutingDimension& capacity_dimension =
routing.GetDimensionOrDie(kCapacity);
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
for (int route_number = 0; route_number < routing.vehicles();
++route_number) {
std::string route_output;
int64 order = routing.Start(route_number);
StringAppendF(&route_output, "Route %d: ", route_number);
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
route_output += "Empty\n";
} else {
while (true) {
IntVar* const load_var = capacity_dimension.CumulVar(order);
IntVar* const time_var = time_dimension.CumulVar(order);
StringAppendF(&route_output, " -> %lld Load(%lld) Time(%lld, %lld)",
order, plan.Value(load_var), plan.Min(time_var),
plan.Max(time_var));
if (routing.IsEnd(order)) break;
order = plan.Value(routing.NextVar(order));
}
}
LOG(INFO) << route_output;
}
}
int main(int argc, char** argv) {
google::ParseCommandLineFlags( &argc, &argv, true);
CHECK_LT(0, FLAGS_vrp_stops) << "Specify an instance size greater than 0.";
CHECK_LT(0, FLAGS_vrp_orders_per_stop)
<< "Specify an instance size greater than 0.";
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
const int vrp_orders = FLAGS_vrp_stops * FLAGS_vrp_orders_per_stop;
// Nodes are indexed from 0 to vrp_orders, the starts and ends of the routes
// are at node 0.
const RoutingModel::NodeIndex kDepot(0);
RoutingModel routing(vrp_orders + 1, FLAGS_vrp_vehicles);
routing.SetDepot(kDepot);
// Setting first solution heuristic (cheapest addition).
FLAGS_routing_first_solution = "PathCheapestArc";
// Disabling Large Neighborhood Search, comment out to activate it.
FLAGS_routing_no_lns = true;
// Setting up locations.
const int64 kXMax = 100000;
const int64 kYMax = 100000;
const int64 kSpeed = 10;
LocationContainer locations(kSpeed);
for (int stop = 0; stop <= FLAGS_vrp_stops; ++stop) {
const int num_orders = stop == 0 ? 1 : FLAGS_vrp_orders_per_stop;
locations.AddRandomLocation(kXMax, kYMax, num_orders);
}
// Setting the cost function.
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance));
// Adding capacity dimension constraints.
const int64 kVehicleCapacity = 40;
const int64 kNullCapacitySlack = 0;
RandomDemand demand(routing.nodes(), kDepot);
demand.Initialize();
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
kNullCapacitySlack, kVehicleCapacity,
/*fix_start_cumul_to_zero=*/true, kCapacity);
// Adding time dimension constraints.
const int64 kStopTime = 300;
const int64 kHorizon = 24 * 3600;
StopServiceTimePlusTransition time(
kStopTime,
locations,
NewPermanentCallback(&locations, &LocationContainer::ManhattanTime));
routing.AddDimension(
NewPermanentCallback(&time, &StopServiceTimePlusTransition::Compute),
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
// Adding time windows, for the sake of simplicty same for each stop.
ACMRandom randomizer(GetSeed());
const int64 kTWDuration = 5 * 3600;
for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) {
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
for (int stop_order = 0;
stop_order < FLAGS_vrp_orders_per_stop;
++stop_order) {
const int order = stop * FLAGS_vrp_orders_per_stop + stop_order + 1;
routing.CumulVar(order, kTime)->SetRange(start, start + kTWDuration);
}
}
// Adding resource constraints at order locations.
Solver* const solver = routing.solver();
std::vector<IntervalVar*> intervals;
for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) {
std::vector<IntervalVar*> stop_intervals;
for (int stop_order = 0;
stop_order < FLAGS_vrp_orders_per_stop;
++stop_order) {
const int order = stop * FLAGS_vrp_orders_per_stop + stop_order + 1;
IntervalVar* const interval =
solver->MakeFixedDurationIntervalVar(
0, kHorizon, kStopTime, true, StrCat("Order", order));
intervals.push_back(interval);
stop_intervals.push_back(interval);
// Link order and interval.
IntVar* const order_start = routing.CumulVar(order, kTime);
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(
NewPermanentCallback(&locations,
&LocationContainer::SameLocationFromIndex,
order),
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> location_usage(stop_intervals.size(), 1);
solver->AddConstraint(
solver->MakeCumulative(
stop_intervals, location_usage, 1, StrCat("Client", stop)));
}
// Minimizing route duration.
for (int vehicle = 0 ; vehicle < routing.vehicles(); ++vehicle) {
routing.AddVariableMinimizedByFinalizer(
routing.CumulVar(routing.End(vehicle), kTime));
}
// Adding penalty costs to allow skipping orders.
const int64 kPenalty = 100000;
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
order < routing.nodes(); ++order) {
std::vector<RoutingModel::NodeIndex> orders(1, order);
routing.AddDisjunction(orders, kPenalty);
}
// Solve, returns a solution if any (owned by RoutingModel).
const Assignment* solution = routing.Solve();
if (solution != NULL) {
DisplayPlan(routing, *solution);
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 0;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -63,16 +63,18 @@ void BuildLinearProgrammingMaxExample(MPSolver::OptimizationProblemType type) {
new_proto::MPModelRequest model_request;
model_request.mutable_model()->CopyFrom(model_proto);
#if defined(USE_GLPK)
if (type == MPSolver::GLPK_LINEAR_PROGRAMMING) {
model_request.set_solver_type(new_proto::MPModelRequest::GLPK_LINEAR_PROGRAMMING);
#if defined(USE_GLOP)
if (type == MPSolver::GLOP_LINEAR_PROGRAMMING) {
model_request.set_solver_type(
new_proto::MPModelRequest::GLOP_LINEAR_PROGRAMMING);
}
#endif // USE_GLPK
#if defined(USE_CLP)
#endif // USE_GLOP
#if defined(USE_CLP)
if (type == MPSolver::CLP_LINEAR_PROGRAMMING) {
model_request.set_solver_type(new_proto::MPModelRequest::CLP_LINEAR_PROGRAMMING);
model_request.set_solver_type(
new_proto::MPModelRequest::CLP_LINEAR_PROGRAMMING);
}
#endif // USE_CLP
#endif // USE_CLP
new_proto::MPSolutionResponse solution_response;
MPSolver::SolveWithProto(model_request, &solution_response);
@@ -88,14 +90,14 @@ void BuildLinearProgrammingMaxExample(MPSolver::OptimizationProblemType type) {
}
void RunAllExamples() {
#if defined(USE_GLPK)
LOG(INFO) << "----- Running Max Example with GLPK -----";
BuildLinearProgrammingMaxExample(MPSolver::GLPK_LINEAR_PROGRAMMING);
#endif // USE_GLPK
#if defined(USE_CLP)
#if defined(USE_GLOP)
LOG(INFO) << "----- Running Max Example with GLOP -----";
BuildLinearProgrammingMaxExample(MPSolver::GLOP_LINEAR_PROGRAMMING);
#endif // USE_GLOP
#if defined(USE_CLP)
LOG(INFO) << "----- Running Max Example with Coin LP -----";
BuildLinearProgrammingMaxExample(MPSolver::CLP_LINEAR_PROGRAMMING);
#endif // USE_CLP
#endif // USE_CLP
}
} // namespace operations_research

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -26,6 +26,7 @@
#include "base/filelinereader.h"
#include "base/split.h"
#include "constraint_solver/constraint_solver.h"
#include "constraint_solver/hybrid.h"
DEFINE_string(
data_file, "",
@@ -171,7 +172,9 @@ class MultiDimKnapsackData {
}
break;
}
case 6: { break; }
case 6: {
break;
}
}
} else {
// 0 = init
@@ -304,7 +307,7 @@ void SolveKnapsack(MultiDimKnapsackData* const data) {
if (FLAGS_simplex_frequency > 0) {
SearchMonitor* const simplex =
solver.MakeSimplexConstraint(FLAGS_simplex_frequency);
MakeSimplexConstraint(&solver, FLAGS_simplex_frequency);
monitors.push_back(simplex);
}

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2013 Google
// Copyright 2010-2014 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at