diff --git a/examples/dotnet/CoverRectangleSat.cs b/examples/dotnet/CoverRectangleSat.cs index b09bbe03be..77b38d89d0 100644 --- a/examples/dotnet/CoverRectangleSat.cs +++ b/examples/dotnet/CoverRectangleSat.cs @@ -100,7 +100,7 @@ class CoverRectangleSat bool solution_found = status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible; if (solution_found) { - char[][] output = new char[sizeY][]; + char[][] output = new char [sizeY][]; foreach (var y in Enumerable.Range(0, sizeY)) { @@ -123,7 +123,8 @@ class CoverRectangleSat { if (output[y][x] != ' ') { - Console.WriteLine(string.Format("Error at position x={0} y{1}, found {2}", x, y, output[y][x])); + Console.WriteLine( + string.Format("Error at position x={0} y{1}, found {2}", x, y, output[y][x])); } output[y][x] = c; } @@ -139,10 +140,11 @@ class CoverRectangleSat static void Main() { - foreach (int numSquares in Enumerable.Range(1, 15)) + foreach (int numSquares in Enumerable.Range(1, 15)) { Console.WriteLine("Trying with size = {0}", numSquares); - if (CoverRectangle(numSquares)) break; + if (CoverRectangle(numSquares)) + break; } - } + } } diff --git a/examples/dotnet/cscvrptw.cs b/examples/dotnet/cscvrptw.cs index e1f3f08779..61f13c836e 100644 --- a/examples/dotnet/cscvrptw.cs +++ b/examples/dotnet/cscvrptw.cs @@ -298,13 +298,13 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { String route = "Vehicle " + vehicle + ": "; - long order = model.Start(vehicle); - if (model.IsEnd(solution.Value(model.NextVar(order)))) + if (!model.IsVehicleUsed(solution, vehicle)) { route += "Empty"; } else { + long order = model.Start(vehicle); for (; !model.IsEnd(order); order = solution.Value(model.NextVar(order))) { IntVar local_load = capacity_dimension.CumulVar(order); diff --git a/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java b/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java index e5ce955b7c..7f3dae049c 100644 --- a/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java +++ b/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java @@ -21,6 +21,7 @@ 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.RoutingSearchStatus; import com.google.ortools.constraintsolver.main; import java.util.ArrayList; import java.util.List; @@ -29,70 +30,139 @@ import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; import java.util.logging.Logger; -// A pair class - -class Pair { - final K first; - final V second; - - public static Pair of(K element0, V element1) { - return new Pair(element0, element1); - } - - public Pair(K element0, V element1) { - this.first = element0; - this.second = element1; - } -} - /** * Sample showing how to model and solve a capacitated vehicle routing problem with time windows - * using the swig-wrapped version of the vehicle routing library in src/constraint_solver. + * using the swig-wrapped version of the vehicle routing library in + * //ortools/constraint_solver. */ public class CapacitatedVehicleRoutingProblemWithTimeWindows { - private static Logger logger = + private static final Logger logger = Logger.getLogger(CapacitatedVehicleRoutingProblemWithTimeWindows.class.getName()); - // Locations representing either an order location or a vehicle route - // start/end. - private List> locations = new ArrayList(); + // A pair class + static class Pair { + final K first; + final V second; - // Quantity to be picked up for each order. - private List orderDemands = new ArrayList(); - // Time window in which each order must be performed. - private List> orderTimeWindows = new ArrayList(); - // Penalty cost "paid" for dropping an order. - private List orderPenalties = new ArrayList(); + public static Pair of(K element0, V element1) { + return new Pair(element0, element1); + } - // Capacity of the vehicles. - private int vehicleCapacity = 0; - // Latest time at which each vehicle must end its tour. - private List vehicleEndTime = new ArrayList(); - // Cost per unit of distance of each vehicle. - private List vehicleCostCoefficients = new ArrayList(); - // Vehicle start and end indices. They have to be implemented as int[] due - // to the available SWIG-ed interface. - private int vehicleStarts[]; - private int vehicleEnds[]; + public Pair(K element0, V element1) { + this.first = element0; + this.second = element1; + } + } - // Random number generator to produce data. - private final Random randomGenerator = new Random(0xBEEF); + static class DataModel { + // Locations representing either an order location or a vehicle route + // start/end. + public final List> locations = new ArrayList<>(); + + // Quantity to be picked up for each order. + public final List orderDemands = new ArrayList<>(); + // Time window in which each order must be performed. + public final List> orderTimeWindows = new ArrayList<>(); + // Penalty cost "paid" for dropping an order. + public final List orderPenalties = new ArrayList<>(); + + public final int numberOfVehicles = 20; + + // Capacity of the vehicles. + public final int vehicleCapacity = 50; + public final int numberOfOrders = 100; + // Latest time at which each vehicle must end its tour. + public final List vehicleEndTime = new ArrayList<>(); + // Cost per unit of distance of each vehicle. + public final List vehicleCostCoefficients = new ArrayList<>(); + // Vehicle start and end indices. They have to be implemented as int[] due + // to the available SWIG-ed interface. + public int[] vehicleStarts; + public int[] vehicleEnds; + + // Random number generator to produce data. + private final Random randomGenerator = new Random(0xBEEF); + + /** + * Creates order data. Location of the order is random, as well as its demand (quantity), time + * window and penalty. + * + * @param xMax maximum x coordinate in which orders are located. + * @param yMax maximum y coordinate in which orders are located. + * @param demandMax maximum quantity of a demand. + * @param timeWindowMax maximum starting time of the order time window. + * @param timeWindowWidth duration of the order time window. + * @param penaltyMin minimum pernalty cost if order is dropped. + * @param penaltyMax maximum pernalty cost if order is dropped. + */ + private void buildOrders(int xMax, int yMax, int demandMax, int timeWindowMax, + int timeWindowWidth, int penaltyMin, int penaltyMax) { + for (int order = 0; order < numberOfOrders; ++order) { + locations.add( + Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); + orderDemands.add(randomGenerator.nextInt(demandMax + 1)); + int timeWindowStart = randomGenerator.nextInt(timeWindowMax + 1); + orderTimeWindows.add(Pair.of(timeWindowStart, timeWindowStart + timeWindowWidth)); + orderPenalties.add(randomGenerator.nextInt(penaltyMax - penaltyMin + 1) + penaltyMin); + } + } + + /** + * Creates fleet data. Vehicle starting and ending locations are random, as well as vehicle + * costs per distance unit. + * + * @param xMax maximum x coordinate in which orders are located. + * @param yMax maximum y coordinate in which orders are located. + * @param endTime latest end time of a tour of a vehicle. + * @param costCoefficientMax maximum cost per distance unit of a vehicle (minimum is 1), + */ + private void buildFleet(int xMax, int yMax, int endTime, int costCoefficientMax) { + vehicleStarts = new int[numberOfVehicles]; + vehicleEnds = new int[numberOfVehicles]; + for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { + vehicleStarts[vehicle] = locations.size(); + locations.add( + Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); + vehicleEnds[vehicle] = locations.size(); + locations.add( + Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); + vehicleEndTime.add(endTime); + vehicleCostCoefficients.add(randomGenerator.nextInt(costCoefficientMax) + 1); + } + } + + public DataModel() { + final int xMax = 20; + final int yMax = 20; + final int demandMax = 3; + final int timeWindowMax = 24 * 60; + final int timeWindowWidth = 4 * 60; + final int penaltyMin = 50; + final int penaltyMax = 100; + final int endTime = 24 * 60; + final int costCoefficientMax = 3; + buildOrders(xMax, yMax, demandMax, timeWindowMax, timeWindowWidth, penaltyMin, penaltyMax); + buildFleet(xMax, yMax, endTime, costCoefficientMax); + } + } // DataModel /** * Creates a Manhattan Distance evaluator with 'costCoefficient'. * + * @param data Data Model. * @param manager Node Index Manager. * @param costCoefficient The coefficient to apply to the evaluator. */ - private LongBinaryOperator buildManhattanCallback( - RoutingIndexManager manager, int costCoefficient) { + private static LongBinaryOperator buildManhattanCallback( + DataModel data, RoutingIndexManager manager, int costCoefficient) { return new LongBinaryOperator() { - public long applyAsLong(long firstIndex, long secondIndex) { + @Override + public long applyAsLong(long fromIndex, long toIndex) { try { - int firstNode = manager.indexToNode(firstIndex); - int secondNode = manager.indexToNode(secondIndex); - Pair firstLocation = locations.get(firstNode); - Pair secondLocation = locations.get(secondNode); + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + Pair firstLocation = data.locations.get(fromNode); + Pair secondLocation = data.locations.get(toNode); return (long) costCoefficient * (Math.abs(firstLocation.first - secondLocation.first) + Math.abs(firstLocation.second - secondLocation.second)); @@ -104,83 +174,82 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { }; } - /** - * Creates order data. Location of the order is random, as well as its demand (quantity), time - * window and penalty. - * - * @param numberOfOrders number of orders to build. - * @param xMax maximum x coordinate in which orders are located. - * @param yMax maximum y coordinate in which orders are located. - * @param demandMax maximum quantity of a demand. - * @param timeWindowMax maximum starting time of the order time window. - * @param timeWindowWidth duration of the order time window. - * @param penaltyMin minimum pernalty cost if order is dropped. - * @param penaltyMax maximum pernalty cost if order is dropped. - */ - private void buildOrders(int numberOfOrders, int xMax, int yMax, int demandMax, int timeWindowMax, - int timeWindowWidth, int penaltyMin, int penaltyMax) { - logger.info("Building orders."); - for (int order = 0; order < numberOfOrders; ++order) { - locations.add(Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); - orderDemands.add(randomGenerator.nextInt(demandMax + 1)); - int timeWindowStart = randomGenerator.nextInt(timeWindowMax + 1); - orderTimeWindows.add(Pair.of(timeWindowStart, timeWindowStart + timeWindowWidth)); - orderPenalties.add(randomGenerator.nextInt(penaltyMax - penaltyMin + 1) + penaltyMin); + // Print the solution. + static void printSolution( + DataModel data, RoutingModel model, RoutingIndexManager manager, Assignment solution) { + RoutingSearchStatus.Value status = model.status(); + logger.info("Status: " + status); + if (status != RoutingSearchStatus.Value.ROUTING_OPTIMAL + && status != RoutingSearchStatus.Value.ROUTING_SUCCESS) { + logger.warning("No solution found!"); + return; + } + + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Dropped orders + String dropped = ""; + for (int order = 0; order < data.numberOfOrders; ++order) { + if (solution.value(model.nextVar(order)) == order) { + dropped += " " + order; + } + } + if (dropped.length() > 0) { + logger.info("Dropped orders:" + dropped); + } + + // Routes + for (int vehicle = 0; vehicle < data.numberOfVehicles; ++vehicle) { + if (!model.isVehicleUsed(solution, vehicle)) { + continue; + } + long index = model.start(vehicle); + logger.info("Route for Vehicle " + vehicle + ":"); + String route = ""; + RoutingDimension capacityDimension = model.getMutableDimension("capacity"); + RoutingDimension timeDimension = model.getMutableDimension("time"); + while (!model.isEnd(index)) { + IntVar load = capacityDimension.cumulVar(index); + IntVar time = timeDimension.cumulVar(index); + long nodeIndex = manager.indexToNode(index); + route += nodeIndex + " Load(" + solution.value(load) + ")"; + route += " Time(" + solution.min(time) + ", " + solution.max(time) + ") -> "; + index = solution.value(model.nextVar(index)); + } + IntVar load = capacityDimension.cumulVar(index); + IntVar time = timeDimension.cumulVar(index); + long nodeIndex = manager.indexToNode(index); + route += nodeIndex + " Load(" + solution.value(load) + ")"; + route += " Time(" + solution.min(time) + ", " + solution.max(time) + ")"; + logger.info(route); } } - /** - * Creates fleet data. Vehicle starting and ending locations are random, as well as vehicle costs - * per distance unit. - * - * @param numberOfVehicles - * @param xMax maximum x coordinate in which orders are located. - * @param yMax maximum y coordinate in which orders are located. - * @param endTime latest end time of a tour of a vehicle. - * @param capacity capacity of a vehicle. - * @param costCoefficientMax maximum cost per distance unit of a vehicle (mimimum is 1), - */ - private void buildFleet( - int numberOfVehicles, int xMax, int yMax, int endTime, int capacity, int costCoefficientMax) { - logger.info("Building fleet."); - vehicleCapacity = capacity; - vehicleStarts = new int[numberOfVehicles]; - vehicleEnds = new int[numberOfVehicles]; - for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { - vehicleStarts[vehicle] = locations.size(); - locations.add(Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); - vehicleEnds[vehicle] = locations.size(); - locations.add(Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); - vehicleEndTime.add(endTime); - vehicleCostCoefficients.add(randomGenerator.nextInt(costCoefficientMax) + 1); - } - } + public static void main(String[] args) { + Loader.loadNativeLibraries(); - /** Solves the current routing problem. */ - private void solve(final int numberOfOrders, final int numberOfVehicles) { - logger.info( - "Creating model with " + numberOfOrders + " orders and " + numberOfVehicles + " vehicles."); - // Finalizing model - final int numberOfLocations = locations.size(); + // Instantiate the data problem. + final DataModel data = new DataModel(); - RoutingIndexManager manager = - new RoutingIndexManager(numberOfLocations, numberOfVehicles, vehicleStarts, vehicleEnds); + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager( + data.locations.size(), data.numberOfVehicles, data.vehicleStarts, data.vehicleEnds); RoutingModel model = new RoutingModel(manager); // Setting up dimensions final int bigNumber = 100000; - final LongBinaryOperator callback = buildManhattanCallback(manager, 1); - final String timeStr = "time"; - model.addDimension( - model.registerTransitCallback(callback), bigNumber, bigNumber, false, timeStr); - RoutingDimension timeDimension = model.getMutableDimension(timeStr); + final LongBinaryOperator callback = buildManhattanCallback(data, manager, 1); + boolean unused = model.addDimension( + model.registerTransitCallback(callback), bigNumber, bigNumber, false, "time"); + RoutingDimension timeDimension = model.getMutableDimension("time"); LongUnaryOperator demandCallback = new LongUnaryOperator() { - public long applyAsLong(long index) { + @Override + public long applyAsLong(long fromIndex) { try { - int node = manager.indexToNode(index); - if (node < numberOfOrders) { - return orderDemands.get(node); + int fromNode = manager.indexToNode(fromIndex); + if (fromNode < data.numberOfOrders) { + return data.orderDemands.get(fromNode); } return 0; } catch (Throwable throwed) { @@ -189,27 +258,25 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { } } }; - final String capacityStr = "capacity"; - model.addDimension( - model.registerUnaryTransitCallback(demandCallback), 0, vehicleCapacity, true, capacityStr); - RoutingDimension capacityDimension = model.getMutableDimension(capacityStr); + unused = model.addDimension(model.registerUnaryTransitCallback(demandCallback), 0, + data.vehicleCapacity, true, "capacity"); // Setting up vehicles - LongBinaryOperator[] callbacks = new LongBinaryOperator[numberOfVehicles]; - for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { - final int costCoefficient = vehicleCostCoefficients.get(vehicle); - callbacks[vehicle] = buildManhattanCallback(manager, costCoefficient); + LongBinaryOperator[] callbacks = new LongBinaryOperator[data.numberOfVehicles]; + for (int vehicle = 0; vehicle < data.numberOfVehicles; ++vehicle) { + final int costCoefficient = data.vehicleCostCoefficients.get(vehicle); + callbacks[vehicle] = buildManhattanCallback(data, manager, costCoefficient); final int vehicleCost = model.registerTransitCallback(callbacks[vehicle]); model.setArcCostEvaluatorOfVehicle(vehicleCost, vehicle); - timeDimension.cumulVar(model.end(vehicle)).setMax(vehicleEndTime.get(vehicle)); + timeDimension.cumulVar(model.end(vehicle)).setMax(data.vehicleEndTime.get(vehicle)); } // Setting up orders - for (int order = 0; order < numberOfOrders; ++order) { + for (int order = 0; order < data.numberOfOrders; ++order) { timeDimension.cumulVar(order).setRange( - orderTimeWindows.get(order).first, orderTimeWindows.get(order).second); + data.orderTimeWindows.get(order).first, data.orderTimeWindows.get(order).second); long[] orderIndices = {manager.nodeToIndex(order)}; - model.addDisjunction(orderIndices, orderPenalties.get(order)); + int unusedNested = model.addDisjunction(orderIndices, data.orderPenalties.get(order)); } // Solving @@ -219,67 +286,11 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { .setFirstSolutionStrategy(FirstSolutionStrategy.Value.ALL_UNPERFORMED) .build(); - logger.info("Search"); Assignment solution = model.solveWithParameters(parameters); - if (solution != null) { - String output = "Total cost: " + solution.objectiveValue() + "\n"; - // Dropped orders - String dropped = ""; - for (int order = 0; order < numberOfOrders; ++order) { - if (solution.value(model.nextVar(order)) == order) { - dropped += " " + order; - } - } - if (dropped.length() > 0) { - output += "Dropped orders:" + dropped + "\n"; - } - // Routes - for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { - String route = "Vehicle " + vehicle + ": "; - long order = model.start(vehicle); - // Empty route has a minimum of two nodes: Start => End - if (model.isEnd(solution.value(model.nextVar(order)))) { - route += "Empty"; - } else { - for (; !model.isEnd(order); order = solution.value(model.nextVar(order))) { - IntVar load = capacityDimension.cumulVar(order); - IntVar time = timeDimension.cumulVar(order); - route += order + " Load(" + solution.value(load) + ") " - + "Time(" + solution.min(time) + ", " + solution.max(time) + ") -> "; - } - IntVar load = capacityDimension.cumulVar(order); - IntVar time = timeDimension.cumulVar(order); - route += order + " Load(" + solution.value(load) + ") " - + "Time(" + solution.min(time) + ", " + solution.max(time) + ")"; - } - output += route + "\n"; - } - logger.info(output); - } + // Print solution on console. + printSolution(data, model, manager, solution); } - public static void main(String[] args) throws Exception { - Loader.loadNativeLibraries(); - CapacitatedVehicleRoutingProblemWithTimeWindows problem = - new CapacitatedVehicleRoutingProblemWithTimeWindows(); - final int xMax = 20; - final int yMax = 20; - final int demandMax = 3; - final int timeWindowMax = 24 * 60; - final int timeWindowWidth = 4 * 60; - final int penaltyMin = 50; - final int penaltyMax = 100; - final int endTime = 24 * 60; - final int costCoefficientMax = 3; - - final int orders = 100; - final int vehicles = 20; - final int capacity = 50; - - problem.buildOrders( - orders, xMax, yMax, demandMax, timeWindowMax, timeWindowWidth, penaltyMin, penaltyMax); - problem.buildFleet(vehicles, xMax, yMax, endTime, capacity, costCoefficientMax); - problem.solve(orders, vehicles); - } + private CapacitatedVehicleRoutingProblemWithTimeWindows() {} } diff --git a/examples/service/solve_math_opt_model_via_http.py b/examples/service/solve_math_opt_model_via_http.py index 354aa9f305..2142906218 100644 --- a/examples/service/solve_math_opt_model_via_http.py +++ b/examples/service/solve_math_opt_model_via_http.py @@ -30,9 +30,8 @@ _API_KEY = flags.DEFINE_string("api_key", None, "API key for the OR API") def request_example() -> None: - """Endpoint for the Operations Research API.""" - - # Set up the API key and endpoint. + """Run example using MathOpt `remote_http_solve` function.""" + # Set up the API key. api_key = _API_KEY.value if not api_key: print( @@ -50,10 +49,15 @@ def request_example() -> None: model.maximize(2 * x + y) try: result, logs = remote_http_solve.remote_http_solve( - model, mathopt.SolverType.GSCIP, api_key=api_key + model, + mathopt.SolverType.GSCIP, + mathopt.SolveParameters(enable_output=True), + api_key=api_key, ) - print(result) - print(logs) + print("Objective value: ", result.objective_value()) + print("x: ", result.variable_values(x)) + print("y: ", result.variable_values(y)) + print("\n".join(logs)) except remote_http_solve.OptimizationServiceError as err: print(err)