routing: Add VRP Solution Callback (#3300)

This commit is contained in:
Corentin Le Molgat
2022-10-17 18:27:46 +02:00
parent 07ba519e13
commit 2e97e4319b
8 changed files with 867 additions and 0 deletions

View File

@@ -419,6 +419,7 @@ test_cpp_constraint_solver_samples: \
rcpp_vrp_pickup_delivery_fifo \
rcpp_vrp_pickup_delivery_lifo \
rcpp_vrp_resources \
rcpp_vrp_solution_callback \
rcpp_vrp_starts_ends \
rcpp_vrp_time_windows \
rcpp_vrp_with_time_limit

View File

@@ -325,6 +325,7 @@ test_dotnet_constraint_solver_samples: \
rdotnet_VrpPickupDeliveryFifo \
rdotnet_VrpPickupDeliveryLifo \
rdotnet_VrpResources \
rdotnet_VrpSolutionCallback \
rdotnet_VrpStartsEnds \
rdotnet_VrpTimeWindows \
rdotnet_VrpWithTimeLimit

View File

@@ -304,6 +304,7 @@ test_java_constraint_solver_samples: \
rjava_VrpPickupDeliveryFifo \
rjava_VrpPickupDeliveryLifo \
rjava_VrpResources \
rjava_VrpSolutionCallback \
rjava_VrpStartsEnds \
rjava_VrpTimeWindows \
rjava_VrpWithTimeLimit

View File

@@ -138,6 +138,7 @@ test_python_constraint_solver_samples: \
rpy_vrp_pickup_delivery_fifo \
rpy_vrp_pickup_delivery_lifo \
rpy_vrp_resources \
rpy_vrp_solution_callback \
rpy_vrp_starts_ends \
rpy_vrp_time_windows \
rpy_vrp_tokens \

View File

@@ -0,0 +1,200 @@
// Copyright 2010-2022 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.
// [START program]
// [START import]
using System;
using System.Collections.Generic;
using Google.OrTools.ConstraintSolver;
using Google.Protobuf.WellKnownTypes; // Duration
// [END import]
/// <summary>
/// Minimal Vehicle Routing Problem (VRP).
/// The search stop after improving the solution 15 times or after 5 seconds.
/// </summary>
public class VrpSolutionCallback
{
// [START data_model]
class DataModel
{
public long[,] DistanceMatrix = {
{ 0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662 },
{ 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210 },
{ 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754 },
{ 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358 },
{ 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244 },
{ 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708 },
{ 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480 },
{ 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856 },
{ 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514 },
{ 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468 },
{ 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354 },
{ 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844 },
{ 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730 },
{ 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536 },
{ 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194 },
{ 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798 },
{ 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0 }
};
public int VehicleNumber = 4;
public int Depot = 0;
};
// [END data_model]
// [START solution_callback_printer]
/// <summary>
/// Print the solution.
/// </summary>
static void printSolution(ref RoutingIndexManager routingManager, ref RoutingModel routingModel)
{
Console.WriteLine($"Solution objective: {routingModel.CostVar().Value()}:");
// Inspect solution.
long totalDistance = 0;
for (int i = 0; i < routingManager.GetNumberOfVehicles(); ++i)
{
Console.WriteLine("################");
Console.WriteLine($"Route for Vehicle {i}:");
long routeDistance = 0;
long index = routingModel.Start(i);
while (routingModel.IsEnd(index) == false)
{
Console.Write($" {routingManager.IndexToNode(index)} ->");
long previousIndex = index;
index = routingModel.NextVar(index).Value();
routeDistance += routingModel.GetArcCostForVehicle(previousIndex, index, 0);
}
Console.WriteLine($" {routingManager.IndexToNode(index)}");
Console.WriteLine($"Distance of the route: {routeDistance}m");
totalDistance += routeDistance;
}
Console.WriteLine($"Total Distance of all routes: {totalDistance}m");
}
// [END solution_callback_printer]
// [START solution_callback]
class SolutionCallback {
public long[] objectives;
private RoutingIndexManager routingManager;
private RoutingModel routingModel;
private long maxSolution;
private long counter;
public SolutionCallback(
ref RoutingIndexManager manager,
ref RoutingModel routing,
in long limit)
{
routingManager = manager;
routingModel = routing;;
maxSolution = limit;
counter = 0;
objectives = new long[maxSolution];
}
public void Run() {
long objective = routingModel.CostVar().Value();
if(counter == 0 || objective < objectives[counter-1]) {
objectives[counter] = objective;
printSolution(ref routingManager, ref routingModel);
counter++;
}
if(counter >= maxSolution) {
routingModel.solver().FinishCurrentSearch();
}
}
};
// [END solution_callback]
public static void Main(String[] args)
{
// Instantiate the data problem.
// [START data]
DataModel data = new DataModel();
// [END data]
// Create Routing Index Manager
// [START index_manager]
RoutingIndexManager routingManager =
new RoutingIndexManager(data.DistanceMatrix.GetLength(0), data.VehicleNumber, data.Depot);
// [END index_manager]
// Create Routing Model.
// [START routing_model]
RoutingModel routingModel = new RoutingModel(routingManager);
// [END routing_model]
// Create and register a transit callback.
// [START transit_callback]
int transitCallbackIndex = routingModel.RegisterTransitCallback(
(long fromIndex, long toIndex) =>
{
// Convert from routing variable Index to
// distance matrix NodeIndex.
var fromNode = routingManager.IndexToNode(fromIndex);
var toNode = routingManager.IndexToNode(toIndex);
return data.DistanceMatrix[fromNode, toNode];
});
// [END transit_callback]
// Define cost of each arc.
// [START arc_cost]
routingModel.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
// [END arc_cost]
// Add Distance constraint.
// [START distance_constraint]
routingModel.AddDimension(
transitCallbackIndex,
0, // no slack
3000, // vehicle maximum travel distance
true, // start cumul to zero
"Distance");
RoutingDimension distanceDimension = routingModel.GetMutableDimension("Distance");
distanceDimension.SetGlobalSpanCostCoefficient(100);
// [END distance_constraint]
// Attach a solution callback.
// [START attach_callback]
SolutionCallback solutionCallback = new SolutionCallback(ref routingManager, ref routingModel, /*max_solution=*/15);
routingModel.AddAtSolutionCallback(solutionCallback.Run);
// [END attach_callback]
// Setting first solution heuristic.
// [START parameters]
RoutingSearchParameters searchParameters =
operations_research_constraint_solver.DefaultRoutingSearchParameters();
searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.GuidedLocalSearch;
searchParameters.TimeLimit = new Duration { Seconds = 5 };
// [END parameters]
// Solve the problem.
// [START solve]
Assignment solution = routingModel.SolveWithParameters(searchParameters);
// [END solve]
// Print solution on console.
// [START print_solution]
if(solution != null)
{
Console.WriteLine($"Best objective: {solutionCallback.objectives[^1]}");
}
else
{
Console.WriteLine("No solution found !");
}
// [END print_solution]
}
}
// [END program]

View File

@@ -0,0 +1,200 @@
// Copyright 2010-2022 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.
// [START program]
package com.google.ortools.constraintsolver.samples;
// [START import]
import com.google.ortools.Loader;
import com.google.ortools.constraintsolver.Assignment;
import com.google.ortools.constraintsolver.FirstSolutionStrategy;
import com.google.ortools.constraintsolver.LocalSearchMetaheuristic;
import com.google.ortools.constraintsolver.RoutingDimension;
import com.google.ortools.constraintsolver.RoutingIndexManager;
import com.google.ortools.constraintsolver.RoutingModel;
import com.google.ortools.constraintsolver.RoutingSearchParameters;
import com.google.ortools.constraintsolver.main;
import com.google.protobuf.Duration;
import java.util.logging.Logger;
// [END import]
/** Minimal VRP.*/
public class VrpSolutionCallback {
private static final Logger logger = Logger.getLogger(VrpSolutionCallback.class.getName());
// [START data_model]
static class DataModel {
public final long[][] distanceMatrix = {
{0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662},
{548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210},
{776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754},
{696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358},
{582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244},
{274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708},
{502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480},
{194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856},
{308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514},
{194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468},
{536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354},
{502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844},
{388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730},
{354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536},
{468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194},
{776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798},
{662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0},
};
public final int vehicleNumber = 4;
public final int depot = 0;
}
// [END data_model]
// [START solution_callback_printer]
/// @brief Print the solution.
static void printSolution(RoutingIndexManager routingManager, RoutingModel routingModel) {
// Solution cost.
logger.info("################");
logger.info("Solution objective : " + routingModel.costVar().value());
// Inspect solution.
long totalDistance = 0;
for (int i = 0; i < routingManager.getNumberOfVehicles(); ++i) {
logger.info("Route for Vehicle " + i + ":");
long routeDistance = 0;
long index = routingModel.start(i);
String route = "";
while (!routingModel.isEnd(index)) {
route += routingManager.indexToNode(index) + " -> ";
long previousIndex = index;
index = routingModel.nextVar(index).value();
routeDistance += routingModel.getArcCostForVehicle(previousIndex, index, i);
}
logger.info(route + routingManager.indexToNode(index));
logger.info("Distance of the route: " + routeDistance + "m");
totalDistance += routeDistance;
}
logger.info("Total distance of all routes: " + totalDistance + "m");
}
// [END solution_callback_printer]
// [START solution_callback]
static class SolutionCallback implements Runnable {
public final long[] objectives;
private final RoutingIndexManager routingManager;
private final RoutingModel routingModel;
private final int maxSolution;
private int counter;
public SolutionCallback(
final RoutingIndexManager manager,
final RoutingModel routing,
int limit)
{
routingManager = manager;
routingModel = routing;
maxSolution = limit;
counter = 0;
objectives = new long[maxSolution];
}
@Override
public void run() {
long objective = routingModel.costVar().value();
if(counter == 0 || objective < objectives[counter-1]) {
objectives[counter] = objective;
printSolution(routingManager, routingModel);
counter++;
}
if(counter >= maxSolution) {
routingModel.solver().finishCurrentSearch();
}
}
};
// [END solution_callback]
public static void main(String[] args) throws Exception {
Loader.loadNativeLibraries();
// Instantiate the data problem.
// [START data]
final DataModel data = new DataModel();
// [END data]
// Create Routing Index Manager
// [START index_manager]
final RoutingIndexManager routingManager =
new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot);
// [END index_manager]
// Create Routing Model.
// [START routing_model]
final RoutingModel routingModel = new RoutingModel(routingManager);
// [END routing_model]
// Create and register a transit callback.
// [START transit_callback]
final int transitCallbackIndex =
routingModel.registerTransitCallback((long fromIndex, long toIndex) -> {
// Convert from routing variable Index to user NodeIndex.
int fromNode = routingManager.indexToNode(fromIndex);
int toNode = routingManager.indexToNode(toIndex);
return data.distanceMatrix[fromNode][toNode];
});
// [END transit_callback]
// Define cost of each arc.
// [START arc_cost]
routingModel.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
// [END arc_cost]
// Add Distance constraint.
// [START distance_constraint]
routingModel.addDimension(
transitCallbackIndex,
0, // no slack
3000, // vehicle maximum travel distance
true, // start cumul to zero
"Distance");
RoutingDimension distanceDimension = routingModel.getMutableDimension("Distance");
distanceDimension.setGlobalSpanCostCoefficient(100);
// [END distance_constraint]
// Attach a solution callback.
// [START attach_callback]
final SolutionCallback solutionCallback = new SolutionCallback(routingManager, routingModel, 15);
routingModel.addAtSolutionCallback(solutionCallback);
// [END attach_callback]
// Setting first solution heuristic.
// [START parameters]
RoutingSearchParameters searchParameters =
main.defaultRoutingSearchParameters()
.toBuilder()
.setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC)
.setLocalSearchMetaheuristic(LocalSearchMetaheuristic.Value.GUIDED_LOCAL_SEARCH)
.setTimeLimit(Duration.newBuilder().setSeconds(5).build())
.build();
// [END parameters]
// Solve the problem.
// [START solve]
Assignment solution = routingModel.solveWithParameters(searchParameters);
// [END solve]
// Print solution on console.
// [START print_solution]
if(solution != null) {
logger.info("Best objective: " + solutionCallback.objectives[solutionCallback.objectives.length - 1]);
} else {
logger.info("No solution found !");
}
// [END print_solution]
}
}
// [END program]

View File

@@ -0,0 +1,218 @@
// Copyright 2010-2022 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.
// [START program]
// [START import]
#include <algorithm>
#include <cstdint>
#include <functional>
#include <sstream>
#include <vector>
#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"
// [END import]
namespace operations_research {
// [START data_model]
struct DataModel {
const std::vector<std::vector<int64_t>> distance_matrix{
{0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468,
776, 662},
{548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,
1016, 868, 1210},
{776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130,
788, 1552, 754},
{696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,
1164, 560, 1358},
{582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,
1050, 674, 1244},
{274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514,
1050, 708},
{502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514,
1278, 480},
{194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662,
742, 856},
{308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320,
1084, 514},
{194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274,
810, 468},
{536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730,
388, 1152, 354},
{502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308,
650, 274, 844},
{388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536,
388, 730},
{354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342,
422, 536},
{468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342,
0, 764, 194},
{776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388,
422, 764, 0, 798},
{662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536,
194, 798, 0},
};
const int num_vehicles = 4;
const RoutingIndexManager::NodeIndex depot{0};
};
// [END data_model]
//! @brief Print the solution.
//! @param[in] routing_manager Index manager used.
//! @param[in] routing_model Routing solver used.
// [START solution_callback_printer]
void print_solution(
const RoutingIndexManager& routing_manager,
const RoutingModel& routing_model) {
LOG(INFO) << "################";
LOG(INFO) << "Solution objective: " << routing_model.CostVar()->Value();
int64_t total_distance{0};
for (int vehicle_id = 0; vehicle_id < routing_manager.num_vehicles(); ++vehicle_id) {
int64_t index = routing_model.Start(vehicle_id);
LOG(INFO) << "Route for Vehicle " << vehicle_id << ":";
int64_t route_distance{0};
std::stringstream route;
while (routing_model.IsEnd(index) == false) {
route << " " << routing_manager.IndexToNode(index).value() << " ->";
int64_t previous_index = index;
index = routing_model.NextVar(index)->Value();
route_distance += routing_model.GetArcCostForVehicle(previous_index, index,
int64_t{vehicle_id});
}
LOG(INFO) << route.str() << routing_manager.IndexToNode(index).value();
LOG(INFO) << "Distance of the route: " << route_distance << "m";
total_distance += route_distance;
}
LOG(INFO) << "Total distance of all routes: " << total_distance << "m";
}
// [END solution_callback_printer]
// [START solution_callback]
struct SolutionCallback {
const RoutingIndexManager& routing_manager;
const RoutingModel& routing_model;
const int64_t max_solution;
SolutionCallback(
const RoutingIndexManager& manager,
const RoutingModel& model,
const int64_t max_solution):
routing_manager(manager),
routing_model(model),
max_solution(max_solution) {
objectives.reserve(max_solution);
};
~SolutionCallback() = default;
void Run() {
int64_t objective = routing_model.CostVar()->Value();
if(objectives.empty() || objective < objectives.back()) {
objectives.push_back(objective);
print_solution(routing_manager, routing_model);
counter++;
}
if(counter >= max_solution) {
routing_model.solver()->FinishCurrentSearch();
}
}
std::vector<int64_t> objectives = {};
private:
int64_t counter = 0;
};
// [END solution_callback]
void VrpSolutionCallback() {
// Instantiate the data problem.
// [START data]
DataModel data;
// [END data]
// Create Routing Index Manager.
// [START index_manager]
RoutingIndexManager routing_manager(data.distance_matrix.size(), data.num_vehicles,
data.depot);
// [END index_manager]
// Create Routing Model.
// [START routing_model]
RoutingModel routing_model(routing_manager);
// [END routing_model]
// Create and register a transit callback.
// [START transit_callback]
const int transit_callback_index = routing_model.RegisterTransitCallback(
[&data, &routing_manager](int64_t from_index, int64_t to_index) -> int64_t {
// Convert from routing variable Index to distance matrix NodeIndex.
auto from_node = routing_manager.IndexToNode(from_index).value();
auto to_node = routing_manager.IndexToNode(to_index).value();
return data.distance_matrix[from_node][to_node];
});
// [END transit_callback]
// Define cost of each arc.
// [START arc_cost]
routing_model.SetArcCostEvaluatorOfAllVehicles(transit_callback_index);
// [END arc_cost]
// Add Distance constraint.
// [START distance_constraint]
routing_model.AddDimension(
transit_callback_index,
0, // no slack
3000, // vehicle maximum travel distance
true, // start cumul to zero
"Distance");
routing_model.GetMutableDimension("Distance")->SetGlobalSpanCostCoefficient(100);
// [END distance_constraint]
// Attach a solution callback.
// [START attach_callback]
SolutionCallback solution_callback{routing_manager, routing_model, /*max_solution=*/15};
routing_model.AddAtSolutionCallback(std::bind(&SolutionCallback::Run, &solution_callback));
// [END attach_callback]
// Setting first solution heuristic.
// [START parameters]
RoutingSearchParameters search_parameters = DefaultRoutingSearchParameters();
search_parameters.set_first_solution_strategy(
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
search_parameters.set_local_search_metaheuristic(
LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH);
search_parameters.mutable_time_limit()->set_seconds(5);
// [END parameters]
// Solve the problem.
// [START solve]
const Assignment* solution = routing_model.SolveWithParameters(search_parameters);
// [END solve]
// Print solution on console.
// [START print_solution]
if (solution != nullptr) {
LOG(INFO) << "Best objectives: " << std::to_string(solution_callback.objectives.back());
} else {
LOG(INFO) << "No solution found.";
}
// [END print_solution]
}
} // namespace operations_research
int main(int argc, char** argv) {
operations_research::VrpSolutionCallback();
return EXIT_SUCCESS;
}
// [END program]

View File

@@ -0,0 +1,245 @@
#!/usr/bin/env python3
# Copyright 2010-2022 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.
# [START program]
"""Simple Vehicles Routing Problem (VRP).
This is a sample using the routing library python wrapper to solve a VRP
problem.
The solver stop after improving its solution 15 times or after 5 seconds.
Distances are in meters.
"""
# [START import]
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
# [END import]
# [START data_model]
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['distance_matrix'] = [
[
0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354,
468, 776, 662
],
[
548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,
1016, 868, 1210
],
[
776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,
1130, 788, 1552, 754
],
[
696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,
1164, 560, 1358
],
[
582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,
1050, 674, 1244
],
[
274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,
514, 1050, 708
],
[
502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,
514, 1278, 480
],
[
194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,
662, 742, 856
],
[
308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,
320, 1084, 514
],
[
194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,
274, 810, 468
],
[
536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,
730, 388, 1152, 354
],
[
502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,
308, 650, 274, 844
],
[
388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,
536, 388, 730
],
[
354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,
342, 422, 536
],
[
468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,
342, 0, 764, 194
],
[
776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,
388, 422, 764, 0, 798
],
[
662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,
536, 194, 798, 0
],
]
data['num_vehicles'] = 4
data['depot'] = 0
return data
# [END data_model]
# [START solution_callback_printer]
def print_solution(
routing_manager: pywrapcp.RoutingIndexManager,
routing_model: pywrapcp.RoutingModel):
"""Prints solution on console."""
print('################')
print(f'Solution objective: {routing_model.CostVar().Value()}')
total_distance = 0
for vehicle_id in range(routing_manager.GetNumberOfVehicles()):
index = routing_model.Start(vehicle_id)
plan_output = f'Route for vehicle {vehicle_id}:\n'
route_distance = 0
while not routing_model.IsEnd(index):
plan_output += f' {routing_manager.IndexToNode(index)} ->'
previous_index = index
index = routing_model.NextVar(index).Value()
route_distance += routing_model.GetArcCostForVehicle(
previous_index, index, vehicle_id)
plan_output += f' {routing_manager.IndexToNode(index)}\n'
plan_output += f'Distance of the route: {route_distance}m\n'
print(plan_output)
total_distance += route_distance
print(f'Total Distance of all routes: {total_distance}m')
# [END solution_callback_printer]
# [START solution_callback]
def create_routing_monitor(
routing_manager: pywrapcp.RoutingIndexManager,
routing_model: pywrapcp.RoutingModel,
max_solution: int) -> callable:
class RoutingMonitor:
def __init__(self,
manager: pywrapcp.RoutingIndexManager,
model: pywrapcp.RoutingModel,
limit: int):
self._routing_manager = manager
self._routing_model = model
self._counter = 0
self._counter_limit = limit
self.objectives = []
def __call__(self):
objective = int(self._routing_model.CostVar().Value())
if not self.objectives or objective < self.objectives[-1]:
self.objectives.append(objective)
print_solution(self._routing_manager, self._routing_model)
self._counter += 1
if self._counter > self._counter_limit:
self._routing_model.solver().FinishCurrentSearch()
return RoutingMonitor(routing_manager, routing_model, max_solution)
# [END solution_callback]
def main():
"""Entry point of the program."""
# Instantiate the data problem.
# [START data]
data = create_data_model()
# [END data]
# Create the routing index manager.
# [START index_manager]
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'], data['depot'])
# [END index_manager]
# Create Routing Model.
# [START routing_model]
routing_model = pywrapcp.RoutingModel(manager)
# [END routing_model]
# Create and register a transit callback.
# [START transit_callback]
def distance_callback(from_index, to_index):
"""Returns the distance between the two nodes."""
# Convert from routing variable Index to distance matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['distance_matrix'][from_node][to_node]
transit_callback_index = routing_model.RegisterTransitCallback(distance_callback)
# [END transit_callback]
# Define cost of each arc.
# [START arc_cost]
routing_model.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# [END arc_cost]
# Add Distance constraint.
# [START distance_constraint]
dimension_name = 'Distance'
routing_model.AddDimension(
transit_callback_index,
0, # no slack
3000, # vehicle maximum travel distance
True, # start cumul to zero
dimension_name)
distance_dimension = routing_model.GetDimensionOrDie(dimension_name)
distance_dimension.SetGlobalSpanCostCoefficient(100)
# [END distance_constraint]
# Attach a solution callback.
# [START attach_callback]
routing_monitor = create_routing_monitor(manager, routing_model, max_solution=15)
routing_model.AddAtSolutionCallback(routing_monitor)
# [END attach_callback]
# Setting first solution heuristic.
# [START parameters]
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
search_parameters.time_limit.FromSeconds(5)
# [END parameters]
# Solve the problem.
# [START solve]
solution = routing_model.SolveWithParameters(search_parameters)
# [END solve]
# Print solution on console.
# [START print_solution]
if solution:
print(f'Best objective: {routing_monitor.objectives[-1]}')
else:
print('No solution found !')
# [END print_solution]
if __name__ == '__main__':
main()
# [END program]