Sync g3 -> github
- java live-snippet still broken
This commit is contained in:
@@ -1,28 +1,33 @@
|
||||
# Constraint Programming (CP) and Routing Solver
|
||||
|
||||
This directory contains a Constraint Programming (CP) solver and a Vehicle Routing solver.
|
||||
This directory contains a Constraint Programming (CP) solver and a Vehicle
|
||||
Routing solver.
|
||||
|
||||
## CP solver
|
||||
|
||||
[Constraint Programming](http://en.wikipedia.org/wiki/Constraint_programming) is a technology
|
||||
issued from IA and used in Operations Research.
|
||||
[Constraint Programming](http://en.wikipedia.org/wiki/Constraint_programming) is
|
||||
a technology issued from IA and used in Operations Research.
|
||||
|
||||
To begin, skim
|
||||
* [constraint_solver.h](constraint_solver.h):
|
||||
The point of entry for all constraint programming users.
|
||||
* [constraint_solveri.h](constraint_solveri.h):
|
||||
An additional file that helps extending the constraint programming library.
|
||||
|
||||
* [constraint_solver.h](../constraint_solver/constraint_solver.h):
|
||||
Declaration of the core objects for the constraint solver.
|
||||
* [constraint_solveri.h](../constraint_solver/constraint_solveri.h):
|
||||
Collection of objects used to extend the Constraint Solver library.
|
||||
|
||||
### Parameters
|
||||
* [solver_parameters.proto](solver_parameters.proto):
|
||||
The CP solver parameters.
|
||||
* [search_limit.proto](search_limit.proto):
|
||||
Holds parameters to limit the search space within the CP solver, which is important for performance.
|
||||
|
||||
* [solver_parameters.proto](../constraint_solver/solver_parameters.proto):
|
||||
This file contains protocol buffers for all parameters of the CP solver.
|
||||
* [search_limit.proto](../constraint_solver/search_limit.proto):
|
||||
Holds parameters to limit the search space within the CP solver, which is
|
||||
important for performance.
|
||||
|
||||
### Solution
|
||||
* [assignment.proto](assignment.proto):
|
||||
|
||||
* [assignment.proto](../constraint_solver/assignment.proto):
|
||||
Holds the solution of a CP problem.
|
||||
* [demon_profiler.proto](demon_profiler.proto):
|
||||
* [demon_profiler.proto](../constraint_solver/demon_profiler.proto):
|
||||
Holds the timeline and execution profile of constraints and demons (daemons).
|
||||
|
||||
## Routing solver
|
||||
@@ -31,18 +36,26 @@ Holds the timeline and execution profile of constraints and demons (daemons).
|
||||
extension that is implemented on top of the CP solver library.
|
||||
|
||||
To begin, skim
|
||||
* [routing.h](routing.h):
|
||||
The point of entry for routing problems.
|
||||
|
||||
* [routing.h](../constraint_solver/routing.h):
|
||||
The vehicle routing library lets one model and solve generic vehicle routing
|
||||
problems ranging from the Traveling Salesman Problem to more complex problems
|
||||
such as the Capacitated Vehicle Routing Problem with Time Windows.
|
||||
* [routing_flags.h](../constraint_solver/routing_flags.h)
|
||||
|
||||
### Parameters
|
||||
* [routing_parameters.proto](routing_parameters.proto):
|
||||
|
||||
* [routing_parameters.proto](../constraint_solver/routing_parameters.proto):
|
||||
The Vehicle Routing solver parameters.
|
||||
* [routing_enums.proto](../constraint_solver/routing_enums.proto):
|
||||
Enums used to define routing parameters.
|
||||
|
||||
### Solution
|
||||
|
||||
* [assignment.proto](assignment.proto):
|
||||
Holds the solution of a Routing problem.
|
||||
|
||||
### Recipes
|
||||
## Recipes
|
||||
|
||||
You can find a set of code recipes in
|
||||
[the documentation directory](doc/routing.md).
|
||||
[the documentation directory](doc/README.md).
|
||||
|
||||
163
ortools/constraint_solver/doc/CP.md
Normal file
163
ortools/constraint_solver/doc/CP.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Using the CP solver
|
||||
|
||||
|
||||
## Documentation structure
|
||||
|
||||
OR-Tools comes with lots of constraint programming samples given in C++, Python,
|
||||
Java and .Net. Each language have different requirements for the code samples.
|
||||
|
||||
## Basic example
|
||||
|
||||
### C++ code samples
|
||||
|
||||
```cpp
|
||||
#include "ortools/constraint_solver/constraint_solver.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
void SimpleCpProgram() {
|
||||
// Instantiate the solver.
|
||||
Solver solver("CpSimple");
|
||||
|
||||
// Create the variables.
|
||||
const int64 num_vals = 3;
|
||||
IntVar* const x = solver.MakeIntVar(0, num_vals - 1, "x");
|
||||
IntVar* const y = solver.MakeIntVar(0, num_vals - 1, "y");
|
||||
IntVar* const z = solver.MakeIntVar(0, num_vals - 1, "z");
|
||||
|
||||
// Constraint 0: x != y..
|
||||
solver.AddConstraint(solver.MakeAllDifferent({x, y}));
|
||||
LOG(INFO) << "Number of constraints: "
|
||||
<< std::to_string(solver.constraints());
|
||||
|
||||
// Solve the problem.
|
||||
DecisionBuilder* const db = solver.MakePhase(
|
||||
{x, y, z}, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE);
|
||||
|
||||
// Print solution on console.
|
||||
int count = 0;
|
||||
solver.NewSearch(db);
|
||||
while (solver.NextSolution()) {
|
||||
++count;
|
||||
LOG(INFO) << "Solution " << count << ":" << std::endl
|
||||
<< " x=" << x->Value() << " y=" << y->Value()
|
||||
<< " z=" << z->Value();
|
||||
}
|
||||
solver.EndSearch();
|
||||
LOG(INFO) << "Number of solutions found: " << solver.solutions();
|
||||
|
||||
LOG(INFO) << "Advanced usage:" << std::endl
|
||||
<< "Problem solved in " << std::to_string(solver.wall_time())
|
||||
<< "ms" << std::endl
|
||||
<< "Memory usage: " << std::to_string(Solver::MemoryUsage())
|
||||
<< "bytes";
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
operations_research::SimpleCpProgram();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
### Python code samples
|
||||
|
||||
```python
|
||||
"""Simple Constraint optimization example."""
|
||||
|
||||
from __future__ import print_function
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point of the program."""
|
||||
# Instantiate the solver.
|
||||
solver = pywrapcp.Solver('CPSimple')
|
||||
|
||||
# Create the variables.
|
||||
num_vals = 3
|
||||
x = solver.IntVar(0, num_vals - 1, 'x')
|
||||
y = solver.IntVar(0, num_vals - 1, 'y')
|
||||
z = solver.IntVar(0, num_vals - 1, 'z')
|
||||
|
||||
# Constraint 0: x != y.
|
||||
solver.Add(x != y)
|
||||
print('Number of constraints: ', solver.Constraints())
|
||||
|
||||
# Solve the problem.
|
||||
decision_builder = solver.Phase([x, y, z], solver.CHOOSE_FIRST_UNBOUND,
|
||||
solver.ASSIGN_MIN_VALUE)
|
||||
|
||||
# Print solution on console.
|
||||
count = 0
|
||||
solver.NewSearch(decision_builder)
|
||||
while solver.NextSolution():
|
||||
count += 1
|
||||
solution = 'Solution {}:\n'.format(count)
|
||||
for var in [x, y, z]:
|
||||
solution += ' {} = {}'.format(var.Name(), var.Value())
|
||||
print(solution)
|
||||
solver.EndSearch()
|
||||
print('Number of solutions found: ', count)
|
||||
|
||||
print('Advanced usage:')
|
||||
print('Problem solved in ', solver.WallTime(), 'ms')
|
||||
print('Memory usage: ', pywrapcp.Solver.MemoryUsage(), 'bytes')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
### Java code samples
|
||||
|
||||
```java
|
||||
```
|
||||
|
||||
### .Net code samples
|
||||
|
||||
|
||||
```cs
|
||||
using System;
|
||||
using Google.OrTools.ConstraintSolver;
|
||||
|
||||
/// <summary>
|
||||
/// This is a simple CP program.
|
||||
/// </summary>
|
||||
public class SimpleCpProgram {
|
||||
public static void Main(String[] args) {
|
||||
// Instantiate the solver.
|
||||
Solver solver = new Solver("CpSimple");
|
||||
|
||||
// Create the variables.
|
||||
const long numVals = 3;
|
||||
IntVar x = solver.MakeIntVar(0, numVals - 1, "x");
|
||||
IntVar y = solver.MakeIntVar(0, numVals - 1, "y");
|
||||
IntVar z = solver.MakeIntVar(0, numVals - 1, "z");
|
||||
|
||||
// Constraint 0: x != y..
|
||||
solver.Add(solver.MakeAllDifferent(new IntVar[]{x, y}));
|
||||
Console.WriteLine($"Number of constraints: {solver.Constraints()}");
|
||||
|
||||
// Solve the problem.
|
||||
DecisionBuilder db = solver.MakePhase(
|
||||
new IntVar[]{x, y, z},
|
||||
Solver.CHOOSE_FIRST_UNBOUND,
|
||||
Solver.ASSIGN_MIN_VALUE);
|
||||
|
||||
// Print solution on console.
|
||||
int count = 0;
|
||||
solver.NewSearch(db);
|
||||
while (solver.NextSolution()) {
|
||||
++count;
|
||||
Console.WriteLine($"Solution: {count}\n x={x.Value()} y={y.Value()} z={z.Value()}");
|
||||
}
|
||||
solver.EndSearch();
|
||||
Console.WriteLine($"Number of solutions found: {solver.Solutions()}");
|
||||
|
||||
Console.WriteLine("Advanced usage:");
|
||||
Console.WriteLine($"Problem solved in {solver.WallTime()}ms");
|
||||
Console.WriteLine($"Memory usage: {Solver.MemoryUsage()}bytes");
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -12,6 +12,7 @@ Solution with the default Policy (ANY):
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_pickup_delivery.cc](../samples/vrp_pickup_delivery.cc)
|
||||
* [vrp_pickup_delivery.py](../samples/vrp_pickup_delivery.py)
|
||||
* [VrpPickupDelivery.java](../samples/VrpPickupDelivery.java)
|
||||
@@ -22,6 +23,7 @@ Solution with the FIFO Policy:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_pickup_delivery_fifo.cc](../samples/vrp_pickup_delivery_fifo.cc)
|
||||
* [vrp_pickup_delivery_fifo.py](../samples/vrp_pickup_delivery_fifo.py)
|
||||
* [VrpPickupDeliveryFifo.java](../samples/VrpPickupDeliveryFifo.java)
|
||||
@@ -32,6 +34,7 @@ Solution with the LIFO Policy:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_pickup_delivery_lifo.cc](../samples/vrp_pickup_delivery_lifo.cc)
|
||||
* [vrp_pickup_delivery_lifo.py](../samples/vrp_pickup_delivery_lifo.py)
|
||||
* [VrpPickupDeliveryLifo.java](../samples/VrpPickupDeliveryLifo.java)
|
||||
18
ortools/constraint_solver/doc/README.md
Normal file
18
ortools/constraint_solver/doc/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Overview
|
||||
|
||||
You can find here, the documentation for the two following Or-Tools components.
|
||||
|
||||
* [CP Solver](CP.md)
|
||||
|
||||
Constraint programming (CP), is the name given to identifying feasible
|
||||
solutions out of a very large set of candidates, where the problem can be
|
||||
modeled in terms of arbitrary constraints.
|
||||
|
||||
**note:** We **strongly recommend** using the [CP-SAT solver](../../sat)
|
||||
rather than the original CP solver.
|
||||
|
||||
* [Routing Solver](ROUTING.md)
|
||||
|
||||
A specialized library for identifying best vehicle routes given constraints.
|
||||
|
||||
This library extensiond is implemented on top of the CP Solver library.
|
||||
223
ortools/constraint_solver/doc/ROUTING.md
Normal file
223
ortools/constraint_solver/doc/ROUTING.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Using the Vehicle Routing solver
|
||||
|
||||
|
||||
## Documentation structure
|
||||
|
||||
This document presents modeling recipes for the Vehicle routing solver.
|
||||
These are grouped by type:
|
||||
|
||||
* [Travelling Salesman Problem](TSP.md)
|
||||
* [Vehicle Routing Problem](VRP.md)
|
||||
* [Pickup and Delivery Problem](PDP.md)
|
||||
|
||||
OR-Tools comes with lots of vehicle routing samples given in C++, Python, Java
|
||||
and .Net. Each language have different requirements for the code samples.
|
||||
|
||||
## Basic example
|
||||
|
||||
### C++ code samples
|
||||
|
||||
```cpp
|
||||
#include <cmath>
|
||||
#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"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
void SimpleRoutingProgram() {
|
||||
// Instantiate the data problem.
|
||||
int num_location = 5;
|
||||
int num_vehicles = 1;
|
||||
RoutingIndexManager::NodeIndex depot{0};
|
||||
|
||||
// Create Routing Index Manager
|
||||
RoutingIndexManager manager(num_location, num_vehicles, depot);
|
||||
|
||||
// Create Routing Model.
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Define cost of each arc.
|
||||
int distance_call_index = routing.RegisterTransitCallback(
|
||||
[&manager](int64 from_index, int64 to_index) -> int64 {
|
||||
// Convert from routing variable Index to user NodeIndex.
|
||||
auto from_node = manager.IndexToNode(from_index).value();
|
||||
auto to_node = manager.IndexToNode(to_index).value();
|
||||
return std::abs(to_node - from_node);
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_call_index);
|
||||
|
||||
// Setting first solution heuristic.
|
||||
RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters();
|
||||
searchParameters.set_first_solution_strategy(
|
||||
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
|
||||
// Solve the problem.
|
||||
const Assignment* solution = routing.SolveWithParameters(searchParameters);
|
||||
|
||||
// Print solution on console.
|
||||
LOG(INFO) << "Objective: " << solution->ObjectiveValue();
|
||||
// Inspect solution.
|
||||
int64 index = routing.Start(0);
|
||||
LOG(INFO) << "Route for Vehicle 0:";
|
||||
int64 route_distance{0};
|
||||
std::ostringstream route;
|
||||
while (routing.IsEnd(index) == false) {
|
||||
route << manager.IndexToNode(index).value() << " -> ";
|
||||
int64 previous_index = index;
|
||||
index = solution->Value(routing.NextVar(index));
|
||||
route_distance +=
|
||||
routing.GetArcCostForVehicle(previous_index, index, int64{0});
|
||||
}
|
||||
LOG(INFO) << route.str() << manager.IndexToNode(index).value();
|
||||
LOG(INFO) << "Distance of the route: " << route_distance << "m";
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
operations_research::SimpleRoutingProgram();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
### Python code samples
|
||||
|
||||
```python
|
||||
"""Vehicle Routing example."""
|
||||
|
||||
from __future__ import print_function
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point of the program."""
|
||||
# Instantiate the data problem.
|
||||
num_locations = 5
|
||||
num_vehicles = 1
|
||||
depot = 0
|
||||
|
||||
# Create the routing index manager.
|
||||
manager = pywrapcp.RoutingIndexManager(
|
||||
num_locations,
|
||||
num_vehicles,
|
||||
depot)
|
||||
|
||||
# Create Routing Model.
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Create and register a transit callback.
|
||||
def distance_callback(from_index, to_index):
|
||||
"""Returns the absolute difference between the two nodes."""
|
||||
# Convert from routing variable Index to user NodeIndex.
|
||||
from_node = int(manager.IndexToNode(from_index))
|
||||
to_node = int(manager.IndexToNode(to_index))
|
||||
return abs(to_node - from_node)
|
||||
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
|
||||
|
||||
# Define cost of each arc.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
|
||||
|
||||
# Setting first solution heuristic.
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
|
||||
# Print solution on console.
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
index = routing.Start(0)
|
||||
plan_output = 'Route for vehicle 0:\n'
|
||||
route_distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += '{} -> '.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)
|
||||
plan_output += '{}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(route_distance)
|
||||
print(plan_output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
### Java code samples
|
||||
|
||||
```java
|
||||
```
|
||||
|
||||
### .Net code samples
|
||||
|
||||
|
||||
```cs
|
||||
using System;
|
||||
using Google.OrTools.ConstraintSolver;
|
||||
|
||||
/// <summary>
|
||||
/// This is a sample using the routing library .Net wrapper.
|
||||
/// </summary>
|
||||
public class SimpleRoutingProgram {
|
||||
public static void Main(String[] args) {
|
||||
// Instantiate the data problem.
|
||||
const int numLocation = 5;
|
||||
const int numVehicles = 1;
|
||||
const int depot = 0;
|
||||
|
||||
// Create Routing Index Manager
|
||||
RoutingIndexManager manager = new RoutingIndexManager(
|
||||
numLocation,
|
||||
numVehicles,
|
||||
depot);
|
||||
|
||||
// Create Routing Model.
|
||||
RoutingModel routing = new RoutingModel(manager);
|
||||
|
||||
// Create and register a transit callback.
|
||||
int transitCallbackIndex = routing.RegisterTransitCallback(
|
||||
(long fromIndex, long toIndex) => {
|
||||
// Convert from routing variable Index to distance matrix NodeIndex.
|
||||
var fromNode = manager.IndexToNode(fromIndex);
|
||||
var toNode = manager.IndexToNode(toIndex);
|
||||
return Math.Abs(toNode - fromNode);
|
||||
});
|
||||
|
||||
// Define cost of each arc.
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
|
||||
|
||||
// Setting first solution heuristic.
|
||||
RoutingSearchParameters searchParameters =
|
||||
operations_research_constraint_solver.DefaultRoutingSearchParameters();
|
||||
searchParameters.FirstSolutionStrategy =
|
||||
FirstSolutionStrategy.Types.Value.PathCheapestArc;
|
||||
|
||||
// Solve the problem.
|
||||
Assignment solution = routing.SolveWithParameters(searchParameters);
|
||||
|
||||
// Print solution on console.
|
||||
Console.WriteLine("Objective: {0}", solution.ObjectiveValue());
|
||||
// Inspect solution.
|
||||
long index = routing.Start(0);
|
||||
Console.WriteLine("Route for Vehicle 0:");
|
||||
long route_distance = 0;
|
||||
while (routing.IsEnd(index) == false) {
|
||||
Console.Write("{0} -> ", manager.IndexToNode((int)index));
|
||||
long previousIndex = index;
|
||||
index = solution.Value(routing.NextVar(index));
|
||||
route_distance += routing.GetArcCostForVehicle(previousIndex, index, 0);
|
||||
}
|
||||
Console.WriteLine("{0}", manager.IndexToNode(index));
|
||||
Console.WriteLine("Distance of the route: {0}m", route_distance);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Misc
|
||||
Images have been generated using [routing_svg.py](routing_svg.py) through bash
|
||||
script [generate_svg.sh](generate_svg.sh).
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
The Vehicle Routing solver can be used to solve a TSP.
|
||||
The Vehicle Routing solver can be used to solve a Travelling Salesman Problem
|
||||
(TSP).
|
||||
|
||||
## Travelling Salesman Problem
|
||||
## Using Locations
|
||||
Data Problem:
|
||||

|
||||
|
||||
@@ -12,6 +13,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [tsp.cc](../samples/tsp.cc)
|
||||
* [tsp.py](../samples/tsp.py)
|
||||
* [Tsp.java](../samples/Tsp.java)
|
||||
@@ -25,6 +27,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [tsp_distance_matrix.cc](../samples/tsp_distance_matrix.cc)
|
||||
* [tsp_distance_matrix.py](../samples/tsp_distance_matrix.py)
|
||||
* [TspDistanceMatrix.java](../samples/TspDistanceMatrix.java)
|
||||
@@ -12,6 +12,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp.cc](../samples/vrp.cc)
|
||||
* [vrp.py](../samples/vrp.py)
|
||||
* [Vrp.java](../samples/Vrp.java)
|
||||
@@ -25,6 +26,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_global_span.cc](../samples/vrp_global_span.cc)
|
||||
* [vrp_global_span.py](../samples/vrp_global_span.py)
|
||||
* [VrpGlobalSpan.java](../samples/VrpGlobalSpan.java)
|
||||
@@ -38,6 +40,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_capacity.cc](../samples/vrp_capacity.cc)
|
||||
* [vrp_capacity.py](../samples/vrp_capacity.py)
|
||||
* [VrpCapacity.java](../samples/VrpCapacity.java)
|
||||
@@ -51,6 +54,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_drop_nodes.cc](../samples/vrp_drop_nodes.cc)
|
||||
* [vrp_drop_nodes.py](../samples/vrp_drop_nodes.py)
|
||||
* [VrpDropNodes.java](../samples/VrpDropNodes.java)
|
||||
@@ -64,6 +68,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_starts_ends.cc](../samples/vrp_starts_ends.cc)
|
||||
* [vrp_starts_ends.py](../samples/vrp_starts_ends.py)
|
||||
* [VrpStartsEnds.java](../samples/VrpStartsEnds.java)
|
||||
@@ -77,6 +82,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_time_windows.cc](../samples/vrp_time_windows.cc)
|
||||
* [vrp_time_windows.py](../samples/vrp_time_windows.py)
|
||||
* [VrpTimeWindows.java](../samples/VrpTimeWindows.java)
|
||||
@@ -90,6 +96,7 @@ Solution:
|
||||

|
||||
|
||||
Samples:
|
||||
|
||||
* [vrp_resources.cc](../samples/vrp_resources.cc)
|
||||
* [vrp_resources.py](../samples/vrp_resources.py)
|
||||
* [VrpResources.java](../samples/VrpResources.java)
|
||||
@@ -1,28 +0,0 @@
|
||||
# Using the CP solver
|
||||
|
||||
## Table of Contents
|
||||
|
||||
OR-Tools comes with lots of constraint programming samples given in C++, Python, Java
|
||||
and .Net. Each language have different requirements for the code samples.
|
||||
|
||||
## Searching for one solution
|
||||
|
||||
### C++ code samples
|
||||
```cpp
|
||||
../samples/simple_cp_program.cc
|
||||
```
|
||||
|
||||
### Python code samples
|
||||
```python
|
||||
../samples/simple_cp_program.py
|
||||
```
|
||||
|
||||
### Java code samples
|
||||
```java
|
||||
../samples/SimpleCpProgram.java
|
||||
```
|
||||
|
||||
### .Net code samples
|
||||
```cs
|
||||
../samples/SimpleCpProgram.cs
|
||||
```
|
||||
@@ -1,28 +0,0 @@
|
||||
# Using the Vehicle Routing solver
|
||||
|
||||
## Table of Contents
|
||||
|
||||
This document presents modeling recipes for the Vehicle routing solver. These are grouped by type:
|
||||
* [Travelling Salesman Problem](tsp.md)
|
||||
* [Vehicle Routing Problem](vrp.md)
|
||||
* [Pickup and Delivery Problem](pdp.md)
|
||||
|
||||
OR-Tools comes with lots of vehicle routing samples given in C++, Python, Java
|
||||
and .Net. Each language have different requirements for the code samples.
|
||||
|
||||
## Searching for one solution
|
||||
|
||||
### C++ code samples
|
||||
[simple_routing_program.cc](../samples/simple_routing_program.cc)
|
||||
|
||||
### Python code samples
|
||||
[simple_routing_program.py](../samples/simple_routing_program.py)
|
||||
|
||||
### Java code samples
|
||||
[SimpleRoutingProgram.java](../samples/SimpleRoutingProgram.java)
|
||||
|
||||
### .Net code samples
|
||||
[SimpleRoutingProgram.cs](../samples/SimpleRoutingProgram.cs)
|
||||
|
||||
## Misc
|
||||
Images have been generated using [routing_svg.py](routing_svg.py) through bash script [generate_svg.sh](generate_svg.sh).
|
||||
@@ -1,6 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# This Python file uses the following encoding: utf-8
|
||||
# Copyright 2018 Google LLC
|
||||
# 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
|
||||
@@ -19,13 +17,13 @@ from __future__ import print_function
|
||||
import argparse
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
# [START data_model]
|
||||
class DataModel: # pylint: disable=too-many-instance-attributes
|
||||
"""Stores the data for the problem."""
|
||||
|
||||
def __init__(self, args):
|
||||
# Locations in block units
|
||||
locations = \
|
||||
@@ -148,20 +146,28 @@ class DataModel: # pylint: disable=too-many-instance-attributes
|
||||
(10, 15), # 15
|
||||
(5, 15), # 16
|
||||
]
|
||||
if args['drop_nodes'] is True:
|
||||
if args['drop_nodes']:
|
||||
self._demands = [0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8]
|
||||
else:
|
||||
self._demands = [0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8]
|
||||
self._pickups_deliveries = [
|
||||
[1, 6], [2, 10], [4, 3], [5, 9], [7, 8], [15, 11], [13, 12], [16, 14],]
|
||||
[1, 6],
|
||||
[2, 10],
|
||||
[4, 3],
|
||||
[5, 9],
|
||||
[7, 8],
|
||||
[15, 11],
|
||||
[13, 12],
|
||||
[16, 14],
|
||||
]
|
||||
|
||||
if args['tsp'] is True:
|
||||
if args['tsp']:
|
||||
self._num_vehicles = 1
|
||||
else:
|
||||
self._num_vehicles = 4
|
||||
self._vehicle_capacities = [15, 15, 15, 15]
|
||||
|
||||
if args['resources'] is True:
|
||||
if args['resources']:
|
||||
self._vehicle_load_time = 5
|
||||
self._vehicle_unload_time = 5
|
||||
|
||||
@@ -247,10 +253,10 @@ class DataModel: # pylint: disable=too-many-instance-attributes
|
||||
# Printer #
|
||||
###########
|
||||
class GoogleColorPalette():
|
||||
"""Google color codes palette"""
|
||||
"""Google color codes palette."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize Google ColorPalette"""
|
||||
"""Initialize Google ColorPalette."""
|
||||
self._colors = [('blue', r'#4285F4'), ('red', r'#EA4335'),
|
||||
('yellow', r'#FBBC05'), ('green', r'#34A853'),
|
||||
('black', r'#101010'), ('white', r'#FFFFFF')]
|
||||
@@ -297,7 +303,7 @@ class SVG():
|
||||
|
||||
@staticmethod
|
||||
def definitions(colors):
|
||||
"""Writes definitions"""
|
||||
"""Writes definitions."""
|
||||
print(r'<!-- Need this definition to make an arrow marker,'
|
||||
' from https://www.w3.org/TR/svg-markers/ -->')
|
||||
print(r'<defs>')
|
||||
@@ -307,8 +313,8 @@ class SVG():
|
||||
'refX="8" refY="8" markerUnits="strokeWidth" markerWidth="5" markerHeight="5" '
|
||||
'orient="auto">'.format(colorname=color[0]))
|
||||
print(
|
||||
r' <path d="M 0 0 L 16 8 L 0 16 z" stroke="none" fill="{color}"/>'.
|
||||
format(color=color[1]))
|
||||
r' <path d="M 0 0 L 16 8 L 0 16 z" stroke="none" fill="{color}"/>'
|
||||
.format(color=color[1]))
|
||||
print(r' </marker>')
|
||||
print(r'</defs>')
|
||||
|
||||
@@ -448,7 +454,7 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
for vehicle_idx, start in enumerate(self._data.starts):
|
||||
del vehicle_idx
|
||||
color = self._color_palette.value_from_name('black')
|
||||
#color = self._color_palette.value(vehicle_idx)
|
||||
# color = self._color_palette.value(vehicle_idx)
|
||||
loc = self._data.locations[start]
|
||||
self._svg.draw_circle(loc, self._radius, self._stroke_width, color,
|
||||
'white')
|
||||
@@ -464,7 +470,7 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
"""Draws all the locations but the depot."""
|
||||
print(r'<!-- Print locations -->')
|
||||
color = self._color_palette.value_from_name('blue')
|
||||
if self._args['starts_ends'] is False:
|
||||
if not self._args['starts_ends']:
|
||||
for idx, loc in enumerate(self._data.locations):
|
||||
if idx == self._data.depot:
|
||||
continue
|
||||
@@ -491,7 +497,7 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
for x, y in zip(loc, [self._radius * 1.2, self._radius * 1.1])
|
||||
]
|
||||
color = self._color_palette.value_from_name('red')
|
||||
#color = self._color_palette.value(int(math.log(demand, 2)))
|
||||
# color = self._color_palette.value(int(math.log(demand, 2)))
|
||||
self._svg.draw_text(demand, position, self._radius, 'none', color)
|
||||
|
||||
def draw_pickups_deliveries(self):
|
||||
@@ -539,8 +545,8 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
color = self._color_palette.value_from_name('black')
|
||||
for node_idx in dropped_nodes:
|
||||
loc = self._data.locations[node_idx]
|
||||
self._svg.draw_circle(loc, self._radius, self._stroke_width,
|
||||
color, 'white')
|
||||
self._svg.draw_circle(loc, self._radius, self._stroke_width, color,
|
||||
'white')
|
||||
self._svg.draw_text(node_idx, loc, self._radius, 'none', color)
|
||||
|
||||
def routes(self):
|
||||
@@ -566,7 +572,7 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
# First print route
|
||||
previous_loc_idx = None
|
||||
for loc_idx in route:
|
||||
if previous_loc_idx != None and previous_loc_idx != loc_idx:
|
||||
if previous_loc_idx and previous_loc_idx != loc_idx:
|
||||
self._svg.draw_polyline(self._data.locations[previous_loc_idx],
|
||||
self._data.locations[loc_idx],
|
||||
self._stroke_width, color, colorname)
|
||||
@@ -589,7 +595,7 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
self.draw_route(route, color, colorname)
|
||||
|
||||
def tw_routes(self):
|
||||
"""Creates the route time window list from the assignment"""
|
||||
"""Creates the route time window list from the assignment."""
|
||||
if self._assignment is None:
|
||||
print('<!-- No solution found. -->')
|
||||
return []
|
||||
@@ -598,7 +604,7 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
tw_routes = []
|
||||
for vehicle_id in range(self._data.num_vehicles):
|
||||
index = self._routing.Start(vehicle_id)
|
||||
#index = self._assignment.Value(self._routing.NextVar(index))
|
||||
# index = self._assignment.Value(self._routing.NextVar(index))
|
||||
loc_route = []
|
||||
tw_route = []
|
||||
while True:
|
||||
@@ -616,16 +622,16 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
return zip(loc_routes, tw_routes)
|
||||
|
||||
def draw_tw_route(self, route_idx, locations, tw_route, color):
|
||||
"""Draws the time windows for a Route"""
|
||||
is_start = -1;
|
||||
"""Draws the time windows for a Route."""
|
||||
is_start = -1
|
||||
for loc_idx, time_window in zip(locations, tw_route):
|
||||
loc = self._data.locations[loc_idx]
|
||||
if loc_idx == 0: # special case for depot
|
||||
position = [
|
||||
x + y
|
||||
for x, y in zip(loc, [
|
||||
self._radius * is_start,
|
||||
self._radius * (1.8 + route_idx)])
|
||||
x + y for x, y in zip(loc, [
|
||||
self._radius * is_start, self._radius * (
|
||||
1.8 + route_idx)
|
||||
])
|
||||
]
|
||||
is_start = 1
|
||||
else:
|
||||
@@ -634,12 +640,14 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
for x, y in zip(loc, [self._radius * 0, self._radius * 1.8])
|
||||
]
|
||||
self._svg.draw_text(
|
||||
'[{t_min}]'.format(t_min=time_window[0]),
|
||||
position,
|
||||
self._radius * 0.75, 'white', color)
|
||||
'[{t_min}]'.format(t_min=time_window[0]),
|
||||
position,
|
||||
self._radius * 0.75,
|
||||
'white',
|
||||
color)
|
||||
|
||||
def draw_tw_routes(self):
|
||||
"""Draws the time window routes"""
|
||||
"""Draws the time window routes."""
|
||||
print(r'<!-- Print time window routes -->')
|
||||
for route_idx, loc_tw in enumerate(self.tw_routes()):
|
||||
print(r'<!-- Print time window route {} -->'.format(route_idx))
|
||||
@@ -647,35 +655,31 @@ class SVGPrinter(): # pylint: disable=too-many-instance-attributes
|
||||
self.draw_tw_route(route_idx, loc_tw[0], loc_tw[1], color)
|
||||
|
||||
def print_to_console(self):
|
||||
"""Prints a full svg document on stdout"""
|
||||
"""Prints a full svg document on stdout."""
|
||||
margin = self._radius * 2 + 2
|
||||
size = [8 * 114, 8 * 80]
|
||||
self._svg.header(size, margin)
|
||||
self._svg.definitions(self._color_palette.colors)
|
||||
self.draw_grid()
|
||||
if self._args['solution'] is False:
|
||||
if self._args['pickup_delivery'] is True:
|
||||
if not self._args['solution']:
|
||||
if self._args['pickup_delivery']:
|
||||
self.draw_pickups_deliveries()
|
||||
self.draw_locations()
|
||||
else:
|
||||
self.draw_routes()
|
||||
self.draw_drop_nodes()
|
||||
if self._args['starts_ends'] is True:
|
||||
if self._args['starts_ends']:
|
||||
self.draw_depots()
|
||||
else:
|
||||
self.draw_depot()
|
||||
if self._args['capacity'] is True:
|
||||
if self._args['capacity']:
|
||||
self.draw_demands()
|
||||
if self._args['drop_nodes'] is True:
|
||||
if self._args['drop_nodes']:
|
||||
self.draw_demands()
|
||||
if (self._args['time_windows'] is True) or (self._args['resources'] is True):
|
||||
if self._args['time_windows'] or self._args['resources']:
|
||||
self.draw_time_windows()
|
||||
if ((
|
||||
(self._args['time_windows'] is True) or
|
||||
(self._args['resources'] is True)
|
||||
) and (
|
||||
self._args['solution'] is True)
|
||||
):
|
||||
if ((self._args['time_windows'] or self._args['resources']) and
|
||||
self._args['solution']):
|
||||
self.draw_tw_routes()
|
||||
self._svg.footer()
|
||||
|
||||
@@ -744,7 +748,7 @@ def main(): # pylint: disable=too-many-locals,too-many-branches
|
||||
data = DataModel(args)
|
||||
# [END data]
|
||||
|
||||
if args['solution'] is False:
|
||||
if not args['solution']:
|
||||
# Print svg on cout
|
||||
printer = SVGPrinter(args, data)
|
||||
printer.print_to_console()
|
||||
@@ -752,7 +756,7 @@ def main(): # pylint: disable=too-many-locals,too-many-branches
|
||||
|
||||
# Create the routing index manager.
|
||||
# [START index_manager]
|
||||
if args['starts_ends'] is True:
|
||||
if args['starts_ends']:
|
||||
manager = pywrapcp.RoutingIndexManager(
|
||||
len(data.locations), data.num_vehicles, data.starts, data.ends)
|
||||
else:
|
||||
@@ -796,29 +800,29 @@ def main(): # pylint: disable=too-many-locals,too-many-branches
|
||||
demand_callback_index = routing.RegisterUnaryTransitCallback(
|
||||
demand_callback)
|
||||
|
||||
if (args['time_windows'] is True) or (args['resources'] is True):
|
||||
if args['time_windows'] or args['resources']:
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(time_callback_index)
|
||||
else:
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_callback_index)
|
||||
|
||||
if (args['global_span'] is True) or (args['pickup_delivery'] is True):
|
||||
if args['global_span'] or args['pickup_delivery']:
|
||||
dimension_name = 'Distance'
|
||||
routing.AddDimension(distance_callback_index, 0, 3000, True,
|
||||
dimension_name)
|
||||
distance_dimension = routing.GetDimensionOrDie(dimension_name)
|
||||
distance_dimension.SetGlobalSpanCostCoefficient(100)
|
||||
|
||||
if (args['capacity'] is True) or (args['drop_nodes'] is True):
|
||||
if args['capacity'] or args['drop_nodes']:
|
||||
routing.AddDimensionWithVehicleCapacity(
|
||||
demand_callback_index, 0, data.vehicle_capacities, True, 'Capacity')
|
||||
|
||||
if args['drop_nodes'] is True:
|
||||
if args['drop_nodes']:
|
||||
# Allow to drop nodes.
|
||||
penalty = 1000
|
||||
for node in range(1, len(data.locations)):
|
||||
routing.AddDisjunction([manager.NodeToIndex(node)], penalty)
|
||||
|
||||
if args['pickup_delivery'] is True:
|
||||
if args['pickup_delivery']:
|
||||
dimension_name = 'Distance'
|
||||
routing.AddDimension(distance_callback_index, 0, 3000, True,
|
||||
dimension_name)
|
||||
@@ -834,14 +838,14 @@ def main(): # pylint: disable=too-many-locals,too-many-branches
|
||||
routing.solver().Add(
|
||||
distance_dimension.CumulVar(pickup_index) <=
|
||||
distance_dimension.CumulVar(delivery_index))
|
||||
if args['fifo'] is True:
|
||||
if args['fifo']:
|
||||
routing.SetPickupAndDeliveryPolicyOfAllVehicles(
|
||||
pywrapcp.RoutingModel.FIFO)
|
||||
if args['lifo'] is True:
|
||||
if args['lifo']:
|
||||
routing.SetPickupAndDeliveryPolicyOfAllVehicles(
|
||||
pywrapcp.RoutingModel.LIFO)
|
||||
|
||||
if args['starts_ends'] is True:
|
||||
if args['starts_ends']:
|
||||
dimension_name = 'Distance'
|
||||
routing.AddDimension(distance_callback_index, 0, 2000, True,
|
||||
dimension_name)
|
||||
@@ -849,11 +853,11 @@ def main(): # pylint: disable=too-many-locals,too-many-branches
|
||||
distance_dimension.SetGlobalSpanCostCoefficient(100)
|
||||
|
||||
time = 'Time'
|
||||
if (args['time_windows'] is True) or (args['resources'] is True):
|
||||
if args['time_windows'] or args['resources']:
|
||||
routing.AddDimension(time_callback_index, 30, 30, False, time)
|
||||
time_dimension = routing.GetDimensionOrDie(time)
|
||||
# Add time window constraints for each location except depot
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
# Add time window constraints for each location except depot and 'copy' the
|
||||
# slack var in the solution object (aka Assignment) to print it.
|
||||
for location_idx, time_window in enumerate(data.time_windows):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
@@ -861,8 +865,8 @@ def main(): # pylint: disable=too-many-locals,too-many-branches
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0],
|
||||
time_window[1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Add time window constraints for each vehicle start node
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
# Add time window constraints for each vehicle start node and 'copy' the
|
||||
# slack var in the solution object (aka Assignment) to print it.
|
||||
for vehicle_id in range(data.num_vehicles):
|
||||
index = routing.Start(vehicle_id)
|
||||
time_window = data.time_windows[0]
|
||||
@@ -872,41 +876,37 @@ def main(): # pylint: disable=too-many-locals,too-many-branches
|
||||
|
||||
# Instantiate route start and end times to produce feasible times.
|
||||
for vehicle_id in range(data.num_vehicles):
|
||||
routing.AddVariableMinimizedByFinalizer(
|
||||
time_dimension.CumulVar(routing.End(vehicle_id)))
|
||||
routing.AddVariableMinimizedByFinalizer(
|
||||
time_dimension.CumulVar(routing.Start(vehicle_id)))
|
||||
routing.AddVariableMinimizedByFinalizer(
|
||||
time_dimension.CumulVar(routing.End(vehicle_id)))
|
||||
routing.AddVariableMinimizedByFinalizer(
|
||||
time_dimension.CumulVar(routing.Start(vehicle_id)))
|
||||
|
||||
if args['resources'] is True:
|
||||
if args['resources']:
|
||||
# Add resource constraints at the depot.
|
||||
time_dimension = routing.GetDimensionOrDie(time)
|
||||
solver = routing.solver()
|
||||
intervals = []
|
||||
for i in range(data.num_vehicles):
|
||||
# Add loading time at start of routes
|
||||
intervals.append(solver.FixedDurationIntervalVar(
|
||||
time_dimension.CumulVar(routing.Start(i)),
|
||||
data.vehicle_load_time,
|
||||
'depot_interval')
|
||||
)
|
||||
# Add unloading time at end of routes.
|
||||
intervals.append(solver.FixedDurationIntervalVar(
|
||||
time_dimension.CumulVar(routing.End(i)),
|
||||
data.vehicle_unload_time,
|
||||
'depot_interval ')
|
||||
)
|
||||
# Add loading time at start of routes
|
||||
intervals.append(
|
||||
solver.FixedDurationIntervalVar(
|
||||
time_dimension.CumulVar(routing.Start(i)),
|
||||
data.vehicle_load_time, 'depot_interval'))
|
||||
# Add unloading time at end of routes.
|
||||
intervals.append(
|
||||
solver.FixedDurationIntervalVar(
|
||||
time_dimension.CumulVar(routing.End(i)),
|
||||
data.vehicle_unload_time, 'depot_interval '))
|
||||
|
||||
depot_usage = [1 for i in range(data.num_vehicles * 2)]
|
||||
solver.AddConstraint(solver.Cumulative(
|
||||
intervals,
|
||||
depot_usage,
|
||||
data.depot_capacity,
|
||||
'depot'))
|
||||
solver.AddConstraint(
|
||||
solver.Cumulative(intervals, depot_usage, data.depot_capacity,
|
||||
'depot'))
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
# pylint: disable=no-member
|
||||
if args['pickup_delivery'] is False:
|
||||
if not args['pickup_delivery']:
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
|
||||
else:
|
||||
|
||||
@@ -21,7 +21,6 @@ http://en.wikipedia.org/wiki/Travelling_salesman_problem.
|
||||
from __future__ import print_function
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
from __future__ import print_function
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user