diff --git a/examples/java/RandomTsp.java b/examples/java/RandomTsp.java new file mode 100644 index 0000000000..47d44166d8 --- /dev/null +++ b/examples/java/RandomTsp.java @@ -0,0 +1,136 @@ +// +// Copyright 2010-2017 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. +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.LongLongToLong; +import com.google.ortools.constraintsolver.LongToLong; +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 java.io.*; +import java.text.*; +import java.util.*; + +class RandomTsp { + static { + System.loadLibrary("jniortools"); + } + + static class RandomManhattan extends LongLongToLong { + public RandomManhattan(RoutingIndexManager manager, int size, int seed) { + this.xs = new int[size]; + this.ys = new int[size]; + this.indexManager = manager; + Random generator = new Random(seed); + for (int i = 0; i < size; ++i) { + xs[i] = generator.nextInt(1000); + ys[i] = generator.nextInt(1000); + } + } + + @Override + public long run(long firstIndex, long secondIndex) { + int firstNode = indexManager.indexToNode(firstIndex); + int secondNode = indexManager.indexToNode(secondIndex); + return Math.abs(xs[firstNode] - xs[secondNode]) + Math.abs(ys[firstNode] - ys[secondNode]); + } + + private int[] xs; + private int[] ys; + private RoutingIndexManager indexManager; + } + + static class ConstantCallback extends LongToLong { + @Override + public long run(long index) { + return 1; + } + } + + static void solve(int size, int forbidden, int seed) { + RoutingIndexManager manager = new RoutingIndexManager(size, 1, 0); + RoutingModel routing = new RoutingModel(manager); + + // Setting the cost function. + // Put a permanent callback to the distance accessor here. The callback + // has the following signature: ResultCallback2. + // The two arguments are the from and to node inidices. + LongLongToLong distances = new RandomManhattan(manager, size, seed); + routing.setArcCostEvaluatorOfAllVehicles(routing.registerTransitCallback(distances)); + + // Forbid node connections (randomly). + Random randomizer = new Random(); + long forbidden_connections = 0; + while (forbidden_connections < forbidden) { + long from = randomizer.nextInt(size - 1); + long to = randomizer.nextInt(size - 1) + 1; + if (routing.nextVar(from).contains(to)) { + System.out.println("Forbidding connection " + from + " -> " + to); + routing.nextVar(from).removeValue(to); + ++forbidden_connections; + } + } + + // Add dummy dimension to test API. + routing.addDimension( + routing.registerUnaryTransitCallback(new ConstantCallback()), + size + 1, + size + 1, + true, + "dummy"); + + // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters search_parameters = + RoutingSearchParameters.newBuilder() + .mergeFrom(main.defaultRoutingSearchParameters()) + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + + Assignment solution = routing.solveWithParameters(search_parameters); + if (solution != null) { + // Solution cost. + System.out.println("Cost = " + solution.objectiveValue()); + // Inspect solution. + // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 + int route_number = 0; + for (long node = routing.start(route_number); + !routing.isEnd(node); + node = solution.value(routing.nextVar(node))) { + System.out.print("" + node + " -> "); + } + System.out.println("0"); + } + } + + public static void main(String[] args) throws Exception { + int size = 10; + if (args.length > 0) { + size = Integer.parseInt(args[0]); + } + + int forbidden = 0; + if (args.length > 1) { + forbidden = Integer.parseInt(args[1]); + } + + int seed = 0; + if (args.length > 2) { + seed = Integer.parseInt(args[2]); + } + + solve(size, forbidden, seed); + } +} diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index 3d36ee1425..f155539a3f 100755 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -375,8 +375,13 @@ test_java_algorithms_samples: \ test_java_constraint_solver_samples: \ rjava_SimpleRoutingProgram \ rjava_Tsp \ + rjava_TspDistanceMatrix \ rjava_Vrp \ rjava_VrpCapacity \ + rjava_VrpDropNodes \ + rjava_VrpGlobalSpan \ + rjava_VrpStartsEnds \ + rjava_VrpTimeWindows \ .PHONY: test_java_graph_samples # Build and Run all Java Graph Samples (located in ortools/graph/samples) test_java_graph_samples: \ @@ -477,8 +482,7 @@ test_java_java: \ rjava_LinearProgramming \ rjava_LsApi \ rjava_RabbitsPheasants \ - rjava_Tsp \ - rjava_Vrp + rjava_RandomTsp .PHONY: test_java_pimpl test_java_pimpl: \ diff --git a/ortools/constraint_solver/samples/SimpleRoutingProgram.java b/ortools/constraint_solver/samples/SimpleRoutingProgram.java index 0f9ea22831..fdb740094f 100644 --- a/ortools/constraint_solver/samples/SimpleRoutingProgram.java +++ b/ortools/constraint_solver/samples/SimpleRoutingProgram.java @@ -13,7 +13,6 @@ // [START program] // [START import] -import java.util.logging.Logger; import com.google.ortools.constraintsolver.Assignment; import com.google.ortools.constraintsolver.FirstSolutionStrategy; import com.google.ortools.constraintsolver.LongLongToLong; @@ -21,12 +20,11 @@ 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 java.util.logging.Logger; // [END import] class SimpleRoutingProgram { - static { - System.loadLibrary("jniortools"); - } + static { System.loadLibrary("jniortools");} private static Logger logger = Logger.getLogger(SimpleRoutingProgram.class.getName()); diff --git a/ortools/constraint_solver/samples/Tsp.java b/ortools/constraint_solver/samples/Tsp.java index d99c5f3ad1..be5df8cf1e 100644 --- a/ortools/constraint_solver/samples/Tsp.java +++ b/ortools/constraint_solver/samples/Tsp.java @@ -1,6 +1,4 @@ -// -// Copyright 2010-2017 Google LLC -// +// Copyright 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 @@ -12,125 +10,161 @@ // 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] +import static java.lang.Math.abs; import com.google.ortools.constraintsolver.Assignment; import com.google.ortools.constraintsolver.FirstSolutionStrategy; import com.google.ortools.constraintsolver.LongLongToLong; -import com.google.ortools.constraintsolver.LongToLong; 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 java.io.*; -import java.text.*; -import java.util.*; +import java.util.logging.Logger; +// [END import] -class Tsp { - static { - System.loadLibrary("jniortools"); +/** Minimal TSP.*/ +public class Tsp { + static { System.loadLibrary("jniortools"); } + + private static final Logger logger = Logger.getLogger(Tsp.class.getName()); + + // [START data_model] + static class DataModel { + public DataModel() { + locations = new int[][] { + {4, 4}, + {2, 0}, + {8, 0}, + {0, 1}, + {1, 1}, + {5, 2}, + {7, 2}, + {3, 3}, + {6, 3}, + {5, 5}, + {8, 5}, + {1, 6}, + {2, 6}, + {3, 7}, + {6, 7}, + {0, 8}, + {7, 8}, + }; + // Convert locations in meters using a city block dimension of 114m x 80m. + for (int i = 0; i < locations.length; i++) { + locations[i][0] *= 114; + locations[i][1] *= 80; + } + vehicleNumber = 1; + depot = 0; + } + public final int[][] locations; + public final int vehicleNumber; + public final int depot; } + // [END data_model] - static class RandomManhattan extends LongLongToLong { - public RandomManhattan(RoutingIndexManager manager, int size, int seed) { - this.xs = new int[size]; - this.ys = new int[size]; - this.indexManager = manager; - Random generator = new Random(seed); - for (int i = 0; i < size; ++i) { - xs[i] = generator.nextInt(1000); - ys[i] = generator.nextInt(1000); + // [START manhattan_distance] + /// @brief Manhattan distance implemented as a callback. + /// @details It uses an array of positions and computes + /// the Manhattan distance between the two positions of + /// two different indices. + static class ManhattanDistance extends LongLongToLong { + public ManhattanDistance(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + distanceMatrix_ = new long[data.locations.length][data.locations.length]; + indexManager_ = manager; + for (int fromNode = 0; fromNode < data.locations.length; ++fromNode) { + for (int toNode = 0; toNode < data.locations.length; ++toNode) { + if (fromNode == toNode) { + distanceMatrix_[fromNode][toNode] = 0; + } else { + distanceMatrix_[fromNode][toNode] = + (long) abs(data.locations[toNode][0] - data.locations[fromNode][0]) + + (long) abs(data.locations[toNode][1] - data.locations[fromNode][1]); + } + } } } @Override - public long run(long firstIndex, long secondIndex) { - int firstNode = indexManager.indexToNode(firstIndex); - int secondNode = indexManager.indexToNode(secondIndex); - return Math.abs(xs[firstNode] - xs[secondNode]) + Math.abs(ys[firstNode] - ys[secondNode]); + public long run(long fromIndex, long toIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + int toNode = indexManager_.indexToNode(toIndex); + return distanceMatrix_[fromNode][toNode]; } - - private int[] xs; - private int[] ys; - private RoutingIndexManager indexManager; + private long[][] distanceMatrix_; + private RoutingIndexManager indexManager_; } + // [END manhattan_distance] - static class ConstantCallback extends LongToLong { - @Override - public long run(long index) { - return 1; - } - } - - static void solve(int size, int forbidden, int seed) { - RoutingIndexManager manager = new RoutingIndexManager(size, 1, 0); - RoutingModel routing = new RoutingModel(manager); - - // Setting the cost function. - // Put a permanent callback to the distance accessor here. The callback - // has the following signature: ResultCallback2. - // The two arguments are the from and to node inidices. - LongLongToLong distances = new RandomManhattan(manager, size, seed); - routing.setArcCostEvaluatorOfAllVehicles(routing.registerTransitCallback(distances)); - - // Forbid node connections (randomly). - Random randomizer = new Random(); - long forbidden_connections = 0; - while (forbidden_connections < forbidden) { - long from = randomizer.nextInt(size - 1); - long to = randomizer.nextInt(size - 1) + 1; - if (routing.nextVar(from).contains(to)) { - System.out.println("Forbidding connection " + from + " -> " + to); - routing.nextVar(from).removeValue(to); - ++forbidden_connections; - } - } - - // Add dummy dimension to test API. - routing.addDimension( - routing.registerUnaryTransitCallback(new ConstantCallback()), - size + 1, - size + 1, - true, - "dummy"); - - // Solve, returns a solution if any (owned by RoutingModel). - RoutingSearchParameters search_parameters = - RoutingSearchParameters.newBuilder() - .mergeFrom(main.defaultRoutingSearchParameters()) - .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) - .build(); - - Assignment solution = routing.solveWithParameters(search_parameters); - if (solution != null) { - // Solution cost. - System.out.println("Cost = " + solution.objectiveValue()); - // Inspect solution. - // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 - int route_number = 0; - for (long node = routing.start(route_number); - !routing.isEnd(node); - node = solution.value(routing.nextVar(node))) { - System.out.print("" + node + " -> "); - } - System.out.println("0"); + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + logger.info("Route for Vehicle 0:"); + long routeDistance = 0; + String route = ""; + long index = routing.start(0); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, 0); } + route += manager.indexToNode(routing.end(0)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); } + // [END solution_printer] public static void main(String[] args) throws Exception { - int size = 10; - if (args.length > 0) { - size = Integer.parseInt(args[0]); - } + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] - int forbidden = 0; - if (args.length > 1) { - forbidden = Integer.parseInt(args[1]); - } + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.locations.length, data.vehicleNumber, data.depot); + // [END index_manager] - int seed = 0; - if (args.length > 2) { - seed = Integer.parseInt(args[2]); - } + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] - solve(size, forbidden, seed); + // Define cost of each arc. + // [START arc_cost] + LongLongToLong distanceEvaluator = new ManhattanDistance(data, manager); + int transitCostIndex = routing.registerTransitCallback(distanceEvaluator); + routing.setArcCostEvaluatorOfAllVehicles(transitCostIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] } } +// [END program] diff --git a/ortools/constraint_solver/samples/TspDistanceMatrix.java b/ortools/constraint_solver/samples/TspDistanceMatrix.java new file mode 100644 index 0000000000..5460bba307 --- /dev/null +++ b/ortools/constraint_solver/samples/TspDistanceMatrix.java @@ -0,0 +1,152 @@ +// Copyright 2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.LongLongToLong; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal TSP using distance matrix.*/ +public class TspDistanceMatrix { + static { System.loadLibrary("jniortools"); } + + private static final Logger logger = Logger.getLogger(TspDistanceMatrix.class.getName()); + + // [START data_model] + static class DataModel { + public DataModel() { + distanceMatrix = new long[][] { + {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}, + }; + vehicleNumber = 1; + depot = 0; + } + public final long[][] distanceMatrix; + public final int vehicleNumber; + public final int depot; + } + // [END data_model] + + // [START manhattan_distance] + /// @brief Manhattan distance implemented as a callback. + /// @details It uses an array of positions and computes + /// the Manhattan distance between the two positions of + /// two different indices. + static class ManhattanDistance extends LongLongToLong { + public ManhattanDistance(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + distanceMatrix_ = data.distanceMatrix; + indexManager_ = manager; + } + @Override + public long run(long fromIndex, long toIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + int toNode = indexManager_.indexToNode(toIndex); + return distanceMatrix_[fromNode][toNode]; + } + private long[][] distanceMatrix_; + private RoutingIndexManager indexManager_; + } + // [END manhattan_distance] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + logger.info("Route for Vehicle 0:"); + long routeDistance = 0; + String route = ""; + long index = routing.start(0); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, 0); + } + route += manager.indexToNode(routing.end(0)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + LongLongToLong distanceEvaluator = new ManhattanDistance(data, manager); + int transitCostIndex = routing.registerTransitCallback(distanceEvaluator); + routing.setArcCostEvaluatorOfAllVehicles(transitCostIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/ortools/constraint_solver/samples/Vrp.java b/ortools/constraint_solver/samples/Vrp.java index 5f6835edc3..ab75dadb20 100644 --- a/ortools/constraint_solver/samples/Vrp.java +++ b/ortools/constraint_solver/samples/Vrp.java @@ -10,161 +10,148 @@ // 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. -import static java.lang.Math.abs; +// [START program] +// [START import] import com.google.ortools.constraintsolver.Assignment; import com.google.ortools.constraintsolver.FirstSolutionStrategy; import com.google.ortools.constraintsolver.LongLongToLong; -import com.google.ortools.constraintsolver.RoutingDimension; import com.google.ortools.constraintsolver.RoutingIndexManager; import com.google.ortools.constraintsolver.RoutingModel; -import com.google.ortools.constraintsolver.RoutingDimension; import com.google.ortools.constraintsolver.RoutingSearchParameters; import com.google.ortools.constraintsolver.main; -import java.io.*; +import java.util.logging.Logger; +// [END import] -class DataProblem { - private int[][] locations_; +/** Minimal VRP.*/ +public class Vrp { + static { System.loadLibrary("jniortools"); } - public DataProblem() { - locations_ = - new int[][] { - {4, 4}, {2, 0}, {8, 0}, {0, 1}, {1, 1}, {5, 2}, {7, 2}, {3, 3}, {6, 3}, {5, 5}, {8, 5}, - {1, 6}, {2, 6}, {3, 7}, {6, 7}, {0, 8}, {7, 8} - }; + private static final Logger logger = Logger.getLogger(Vrp.class.getName()); - // Compute locations in meters using the block dimension defined as follow - // Manhattan average block: 750ft x 264ft -> 228m x 80m - // here we use: 114m x 80m city block - // src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - int[] cityBlock = {228 / 2, 80}; - for (int i = 0; i < locations_.length; i++) { - locations_[i][0] = locations_[i][0] * cityBlock[0]; - locations_[i][1] = locations_[i][1] * cityBlock[1]; + // [START data_model] + static class DataModel { + public DataModel() { + distanceMatrix = new long[][] { + {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}, + }; + vehicleNumber = 4; + depot = 0; } + public final long[][] distanceMatrix; + public final int vehicleNumber; + public final int depot; } + // [END data_model] - /// @brief Gets the number of vehicles. - public int getVehicleNumber() { - return 4; - } - /// @brief Gets the locations. - public int[][] getLocations() { - return locations_; - } - /// @brief Gets the number of locations. - public int getLocationNumber() { - return locations_.length; - } - /// @brief Gets the depot NodeIndex. - public int getDepot() { - return 0; - } -} - -/// @brief Manhattan distance implemented as a callback. -/// @details It uses an array of positions and computes -/// the Manhattan distance between the two positions of -/// two different indices. -class ManhattanDistance extends LongLongToLong { - private int[][] distances; - private RoutingIndexManager indexManager; - - public ManhattanDistance(DataProblem data, RoutingIndexManager manager) { - // precompute distance between location to have distance callback in O(1) - distances = new int[data.getLocationNumber()][data.getLocationNumber()]; - indexManager = manager; - for (int fromNode = 0; fromNode < data.getLocationNumber(); ++fromNode) { - for (int toNode = 0; toNode < data.getLocationNumber(); ++toNode) { - if (fromNode == toNode) distances[fromNode][toNode] = 0; - else - distances[fromNode][toNode] = - abs(data.getLocations()[toNode][0] - data.getLocations()[fromNode][0]) - + abs(data.getLocations()[toNode][1] - data.getLocations()[fromNode][1]); - } + // [START manhattan_distance] + /// @brief Manhattan distance implemented as a transit callback. + /// @details It uses an array of positions and computes + /// the Manhattan distance between the two positions of + /// two different indices. + static class ManhattanDistance extends LongLongToLong { + public ManhattanDistance(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + distanceMatrix_ = data.distanceMatrix; + indexManager_ = manager; } + @Override + public long run(long fromIndex, long toIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + int toNode = indexManager_.indexToNode(toIndex); + return distanceMatrix_[fromNode][toNode]; + } + private long[][] distanceMatrix_; + private RoutingIndexManager indexManager_; } + // [END manhattan_distance] - @Override - /// @brief Returns the manhattan distance between the two nodes. - public long run(long fromIndex, long toIndex) { - int fromNode = indexManager.indexToNode(fromIndex); - int toNode = indexManager.indexToNode(toIndex); - return distances[fromNode][toNode]; - } -} - -class Vrp { - static { - System.loadLibrary("jniortools"); - } - - /// @brief Add Global Span constraint. - static void addDistanceDimension(RoutingModel routing, DataProblem data, int distanceIndex) { - String distance = "Distance"; - routing.addDimension( - distanceIndex, - 0, // null slack - 3000, // maximum distance per vehicle - true, // start cumul to zero - distance); - RoutingDimension distanceDimension = routing.getDimensionOrDie(distance); - // Try to minimize the max distance among vehicles. - // /!\ It doesn't mean the standard deviation is minimized - distanceDimension.setGlobalSpanCostCoefficient(100); - } - - /// @brief Print the solution + // [START solution_printer] + /// @brief Print the solution. static void printSolution( - DataProblem data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { // Solution cost. - System.out.println("Objective : " + solution.objectiveValue()); + logger.info("Objective : " + solution.objectiveValue()); // Inspect solution. - for (int i = 0; i < data.getVehicleNumber(); ++i) { - System.out.println("Route for Vehicle " + i + ":"); - long distance = 0; - for (long index = routing.start(i); !routing.isEnd(index); ) { - System.out.print(manager.indexToNode((int) index) + " -> "); - + long totalDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + long index = routing.start(i); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; long previousIndex = index; index = solution.value(routing.nextVar(index)); - distance += routing.getArcCostForVehicle(previousIndex, index, i); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); } - System.out.println(manager.indexToNode((int) routing.end(i))); - System.out.println("Distance of the route: " + distance + "m"); + route += manager.indexToNode(routing.end(i)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; } + logger.info("Total Distance of all routes: " + totalDistance + "m"); } + // [END solution_printer] - /// @brief Solves the current routing problem. - static void solve() { + public static void main(String[] args) throws Exception { // Instantiate the data problem. - DataProblem data = new DataProblem(); + // [START data] + final DataModel data = new DataModel(); + // [END data] - // Create Routing Model + // Create Routing Index Manager + // [START index_manager] RoutingIndexManager manager = - new RoutingIndexManager(data.getLocationNumber(), data.getVehicleNumber(), data.getDepot()); + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] RoutingModel routing = new RoutingModel(manager); + // [END routing_model] - // Setting the cost function. - // [todo]: protect callback from the GC + // Define cost of each arc. + // [START arc_cost] LongLongToLong distanceEvaluator = new ManhattanDistance(data, manager); - int distanceIndex = routing.registerTransitCallback(distanceEvaluator); - routing.setArcCostEvaluatorOfAllVehicles(distanceIndex); - addDistanceDimension(routing, data, distanceIndex); + int transitCostIndex = routing.registerTransitCallback(distanceEvaluator); + routing.setArcCostEvaluatorOfAllVehicles(transitCostIndex); + // [END arc_cost] - // Setting first solution heuristic (cheapest addition). - RoutingSearchParameters search_parameters = - RoutingSearchParameters.newBuilder() - .mergeFrom(main.defaultRoutingSearchParameters()) + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) .build(); + // [END parameters] - Assignment solution = routing.solveWithParameters(search_parameters); + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] printSolution(data, routing, manager, solution); - } - - /// @brief Entry point of the program. - public static void main(String[] args) throws Exception { - solve(); + // [END print_solution] } } +// [END program] diff --git a/ortools/constraint_solver/samples/VrpCapacity.java b/ortools/constraint_solver/samples/VrpCapacity.java index 6b4244ebd6..be8c2418f0 100644 --- a/ortools/constraint_solver/samples/VrpCapacity.java +++ b/ortools/constraint_solver/samples/VrpCapacity.java @@ -10,6 +10,7 @@ // 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] import com.google.ortools.constraintsolver.Assignment; diff --git a/ortools/constraint_solver/samples/VrpDropNodes.java b/ortools/constraint_solver/samples/VrpDropNodes.java new file mode 100644 index 0000000000..d3d985b9ef --- /dev/null +++ b/ortools/constraint_solver/samples/VrpDropNodes.java @@ -0,0 +1,213 @@ +// Copyright 2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.LongLongToLong; +import com.google.ortools.constraintsolver.LongToLong; +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 java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpDropNodes { + static { System.loadLibrary("jniortools"); } + + private static final Logger logger = Logger.getLogger(VrpDropNodes.class.getName()); + + // [START data_model] + static class DataModel { + public DataModel() { + distanceMatrix = new long[][] { + {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}, + }; + demands = new long[] {0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8}; + vehicleNumber = 4; + vehicleCapacities = new long[] {15, 15, 15, 15}; + depot = 0; + } + public final long[][] distanceMatrix; + public final long[] demands; + public final int vehicleNumber; + public final long[] vehicleCapacities; + public final int depot; + } + // [END data_model] + + // [START manhattan_distance] + /// @brief Manhattan distance implemented as a transit callback. + /// @details It uses an array of positions and computes + /// the Manhattan distance between the two positions of + /// two different indices. + static class ManhattanDistance extends LongLongToLong { + public ManhattanDistance(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + distanceMatrix_ = data.distanceMatrix; + indexManager_ = manager; + } + @Override + public long run(long fromIndex, long toIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + int toNode = indexManager_.indexToNode(toIndex); + return distanceMatrix_[fromNode][toNode]; + } + private long[][] distanceMatrix_; + private RoutingIndexManager indexManager_; + } + // [END manhattan_distance] + + // [START demands] + static class DemandCallback extends LongToLong { + public DemandCallback(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + demands_ = data.demands; + indexManager_ = manager; + } + @Override + public long run(long fromIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + return demands_[fromNode]; + } + private long[] demands_; + private RoutingIndexManager indexManager_; + } + // [END demands] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Display dropped nodes. + String droppedNodes = "Dropped nodes:"; + for (int node = 0; node < routing.size(); ++node) { + if (routing.isStart(node) || routing.isEnd(node)) { + continue; + } + if (solution.value(routing.nextVar(node)) == node) { + droppedNodes += " " + manager.indexToNode(node); + } + } + logger.info(droppedNodes); + // Display routes + // Inspect solution. + long totalDistance = 0; + long totalLoad = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + long routeLoad = 0; + String route = ""; + while (!routing.isEnd(index)) { + long nodeIndex = manager.indexToNode(index); + routeLoad += data.demands[(int) nodeIndex]; + route += nodeIndex + " Load(" + routeLoad + ") -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + route += manager.indexToNode(routing.end(i)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + totalLoad += routeLoad; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + logger.info("Total Load of all routes: " + totalLoad); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + LongLongToLong distanceEvaluator = new ManhattanDistance(data, manager); + int transitCostIndex = routing.registerTransitCallback(distanceEvaluator); + routing.setArcCostEvaluatorOfAllVehicles(transitCostIndex); + // [END arc_cost] + + // Add Capacity constraint. + // [START capacity_constraint] + LongToLong demandEvaluator = new DemandCallback(data, manager); + int demandCostIndex = routing.registerUnaryTransitCallback(demandEvaluator); + routing.addDimensionWithVehicleCapacity(demandCostIndex, 0, // null capacity slack + data.vehicleCapacities, // vehicle maximum capacities + true, // start cumul to zero + "Capacity"); + // Allow to drop nodes. + long penalty = 1000; + for (int i = 1; i < data.distanceMatrix.length; ++i) { + routing.addDisjunction( + new long[] {manager.nodeToIndex(i)}, penalty); + } + // [END capacity_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/ortools/constraint_solver/samples/VrpGlobalSpan.java b/ortools/constraint_solver/samples/VrpGlobalSpan.java new file mode 100644 index 0000000000..ceb666082c --- /dev/null +++ b/ortools/constraint_solver/samples/VrpGlobalSpan.java @@ -0,0 +1,170 @@ +// Copyright 2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.LongLongToLong; +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 java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpGlobalSpan { + static { System.loadLibrary("jniortools"); } + + private static final Logger logger = Logger.getLogger(VrpGlobalSpan.class.getName()); + + // [START data_model] + static class DataModel { + public DataModel() { + distanceMatrix = new long[][] { + {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}, + }; + demands = new long[] {0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8}; + vehicleNumber = 4; + vehicleCapacities = new long[] {15, 15, 15, 15}; + depot = 0; + } + public final long[][] distanceMatrix; + public final long[] demands; + public final int vehicleNumber; + public final long[] vehicleCapacities; + public final int depot; + } + // [END data_model] + + // [START manhattan_distance] + /// @brief Manhattan distance implemented as a transit callback. + /// @details It uses an array of positions and computes + /// the Manhattan distance between the two positions of + /// two different indices. + static class ManhattanDistance extends LongLongToLong { + public ManhattanDistance(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + distanceMatrix_ = data.distanceMatrix; + indexManager_ = manager; + } + @Override + public long run(long fromIndex, long toIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + int toNode = indexManager_.indexToNode(toIndex); + return distanceMatrix_[fromNode][toNode]; + } + private long[][] distanceMatrix_; + private RoutingIndexManager indexManager_; + } + // [END manhattan_distance] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + long totalDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + LongLongToLong distanceEvaluator = new ManhattanDistance(data, manager); + int transitCostIndex = routing.registerTransitCallback(distanceEvaluator); + routing.setArcCostEvaluatorOfAllVehicles(transitCostIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCostIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/ortools/constraint_solver/samples/VrpStartsEnds.java b/ortools/constraint_solver/samples/VrpStartsEnds.java new file mode 100644 index 0000000000..bb0bfbec68 --- /dev/null +++ b/ortools/constraint_solver/samples/VrpStartsEnds.java @@ -0,0 +1,170 @@ +// Copyright 2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.LongLongToLong; +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 java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpStartsEnds { + static { System.loadLibrary("jniortools"); } + + private static final Logger logger = Logger.getLogger(VrpStartsEnds.class.getName()); + + // [START data_model] + static class DataModel { + public DataModel() { + distanceMatrix = new long[][] { + {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}, + }; + vehicleNumber = 4; + starts = new int[] {1, 2, 15, 16}; + ends = new int[] {0, 0, 0, 0}; + } + public final long[][] distanceMatrix; + public final int vehicleNumber; + public final int[] starts; + public final int[] ends; + } + // [END data_model] + + // [START manhattan_distance] + /// @brief Manhattan distance implemented as a transit callback. + /// @details It uses an array of positions and computes + /// the Manhattan distance between the two positions of + /// two different indices. + static class ManhattanDistance extends LongLongToLong { + public ManhattanDistance(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + distanceMatrix_ = data.distanceMatrix; + indexManager_ = manager; + } + @Override + public long run(long fromIndex, long toIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + int toNode = indexManager_.indexToNode(toIndex); + return distanceMatrix_[fromNode][toNode]; + } + private long[][] distanceMatrix_; + private RoutingIndexManager indexManager_; + } + // [END manhattan_distance] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + long totalDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + long index = routing.start(i); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + route += manager.indexToNode(routing.end(i)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager( + data.distanceMatrix.length, data.vehicleNumber, data.starts, data.ends); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + LongLongToLong distanceEvaluator = new ManhattanDistance(data, manager); + int transitCostIndex = routing.registerTransitCallback(distanceEvaluator); + routing.setArcCostEvaluatorOfAllVehicles(transitCostIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCostIndex, 0, 2000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/ortools/constraint_solver/samples/VrpTimeWindows.java b/ortools/constraint_solver/samples/VrpTimeWindows.java new file mode 100644 index 0000000000..39d121893c --- /dev/null +++ b/ortools/constraint_solver/samples/VrpTimeWindows.java @@ -0,0 +1,205 @@ +// Copyright 2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.LongLongToLong; +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 java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpTimeWindows { + static { System.loadLibrary("jniortools"); } + + private static final Logger logger = Logger.getLogger(VrpTimeWindows.class.getName()); + + // [START data_model] + static class DataModel { + public DataModel() { + timeMatrix = new long[][] { + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + timeWindows = new long[][] { + {0, 0}, + {10, 15}, + {10, 15}, + {5, 10}, + {5, 10}, + {0, 5}, + {5, 10}, + {0, 5}, + {5, 10}, + {0, 5}, + {10, 15}, + {10, 15}, + {0, 5}, + {5, 10}, + {5, 10}, + {10, 15}, + {5, 10}, + }; + vehicleNumber = 4; + depot = 0; + } + public final long[][] timeMatrix; + public final long[][] timeWindows; + public final int vehicleNumber; + public final int depot; + } + // [END data_model] + + // [START times] + static class TimeCallback extends LongLongToLong { + public TimeCallback(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + timeMatrix_ = data.timeMatrix; + indexManager_ = manager; + } + @Override + public long run(long fromIndex, long toIndex) { + int fromNode = indexManager_.indexToNode(fromIndex); + int toNode = indexManager_.indexToNode(toIndex); + return timeMatrix_[fromNode][toNode]; + } + private long[][] timeMatrix_; + private RoutingIndexManager indexManager_; + } + // [END times] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + logger.info("Objective : " + solution.objectiveValue()); + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + long totalTime = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeTime = 0; + String route = ""; + while (!routing.isEnd(index)) { + IntVar timeVar = timeDimension.cumulVar(index); + IntVar slackVar = timeDimension.slackVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ") Slack(" + solution.min(slackVar) + "," + + solution.max(slackVar) + ") -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeTime += routing.getArcCostForVehicle(previousIndex, index, i); + } + IntVar timeVar = timeDimension.cumulVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ")"; + logger.info(route); + logger.info("Time of the route: " + routeTime + "m"); + totalTime += routeTime; + } + logger.info("Total Time of all routes: " + totalTime + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.timeMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + LongLongToLong timeCallback = new TimeCallback(data, manager); + int transitCostIndex = routing.registerTransitCallback(timeCallback); + routing.setArcCostEvaluatorOfAllVehicles(transitCostIndex); + // [END arc_cost] + + // Add Time constraint. + // [START time_constraint] + routing.addDimension(transitCostIndex, // transit callback + 30, // allow waiting time + 30, // vehicle maximum capacities + false, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + // Add time window constraints for each location except depot + // and 'copy' the slack var in the solution object (aka Assignment) to print it + for (int i = 1; i < data.timeWindows.length; ++i) { + long index = manager.nodeToIndex(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[i][0], data.timeWindows[i][1]); + routing.addToAssignment(timeDimension.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 + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[0][0], data.timeWindows[0][1]); + routing.addToAssignment(timeDimension.slackVar(index)); + } + // [END time_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program]