diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index 4a7890e3cd..ecc9989da6 100644 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -334,6 +334,7 @@ test_cc_graph_samples: \ .PHONY: test_cc_linear_solver_samples # Build and Run all C++ LP Samples (located in ortools/linear_solver/samples) test_cc_linear_solver_samples: \ + rcc_assignment_mip \ rcc_bin_packing_mip \ rcc_integer_programming_example \ rcc_linear_programming_example \ diff --git a/makefiles/Makefile.dotnet.mk b/makefiles/Makefile.dotnet.mk index f125ea41db..03a075f4a2 100644 --- a/makefiles/Makefile.dotnet.mk +++ b/makefiles/Makefile.dotnet.mk @@ -520,6 +520,7 @@ test_dotnet_graph_samples: ; .PHONY: test_dotnet_linear_solver_samples # Build and Run all .Net LP Samples (located in ortools/linear_solver/samples) test_dotnet_linear_solver_samples: + $(MAKE) run SOURCE=ortools/linear_solver/samples/AssignmentMip.cs $(MAKE) run SOURCE=ortools/linear_solver/samples/BinPackingMip.cs $(MAKE) run SOURCE=ortools/linear_solver/samples/LinearProgrammingExample.cs $(MAKE) run SOURCE=ortools/linear_solver/samples/MipVarArray.cs diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index 31aa4c9783..3f62b70b67 100644 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -461,6 +461,7 @@ test_java_graph_samples: \ .PHONY: test_java_linear_solver_samples # Build and Run all Java LP Samples (located in ortools/linear_solver/samples) test_java_linear_solver_samples: \ + rjava_AssignmentMip \ rjava_BinPackingMip \ rjava_LinearProgrammingExample \ rjava_MipVarArray \ diff --git a/makefiles/Makefile.python.mk b/makefiles/Makefile.python.mk index b2e3073bb1..dbcfa92105 100644 --- a/makefiles/Makefile.python.mk +++ b/makefiles/Makefile.python.mk @@ -591,6 +591,7 @@ test_python_graph_samples: \ .PHONY: test_python_linear_solver_samples # Run all Python LP Samples (located in ortools/linear_solver/samples) test_python_linear_solver_samples: \ + rpy_assignment_mip \ rpy_bin_packing_mip \ rpy_integer_programming_example \ rpy_linear_programming_example \ diff --git a/ortools/linear_solver/samples/AssignmentMip.cs b/ortools/linear_solver/samples/AssignmentMip.cs new file mode 100644 index 0000000000..fd890e6504 --- /dev/null +++ b/ortools/linear_solver/samples/AssignmentMip.cs @@ -0,0 +1,116 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// 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. + +using System; +using Google.OrTools.LinearSolver; + +public class AssignmentMip +{ + static void Main() + { + // Data. + // [START data_model] + int[,] costs = { + {90, 80, 75, 70}, + {35, 85, 55, 65}, + {125, 95, 90, 95}, + {45, 110, 95, 115}, + {50, 100, 90, 100}, + }; + int numWorkers = costs.GetLength(0); + int numTasks = costs.GetLength(1); + // [END data_model] + + // Model. + // [START model] + Solver solver = Solver.CreateSolver("AssignmentMip", "CBC_MIXED_INTEGER_PROGRAMMING"); + // [END model] + + // Variables. + // [START variables] + // x[i, j] is an array of 0-1 variables, which will be 1 + // if worker i is assigned to task j. + Variable[,] x = new Variable[data.NumItems, data.NumBins]; + for (int i = 0; i < numWorkers; ++i) + { + for (int j = 0; j < numTasks; ++j) + { + x[i, j] = solver.MakeIntVar(0, 1, $"worker_{i}_task_{j}"); + } + } + // [END variables] + + // Constraints + // [START constraints] + // Each worker is assigned to at most one task. + for (int i = 0; i < numWorkers; ++i) + { + Constraint constraint = solver.MakeConstraint(0, 1, ""); + for (int j = 0; j < numTasks; ++j) + { + constraint.SetCoefficient(x[i, j], 1); + } + } + // Each task is assigned to exactly one worker. + for (int j = 0; j < numTasks; ++j) + { + Constraint constraint = solver.MakeConstraint(1, 1, ""); + for (int i = 0; i < numWorkers; ++i) + { + constraint.SetCoefficient(x[i, j], 1); + } + } + // [END constraints] + + // Objective + // [START objective] + Objective objective = solver.Objective(); + for (int i = 0; i < numWorkers; ++i) + { + for (int j = 0; j < numTasks; ++j) + { + objective.SetCoefficient(x[i, j], 1); + } + } + objective.SetMinimization(); + // [END objective] + + // Solve + // [START solve] + Solver.ResultStatus resultStatus = solver.Solve(); + // [END solve] + + // Print solution. + // [START print_solution] + // Check that the problem has a feasible solution. + if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) + { + Console.WriteLine($"Total cost: {solver.ObjectiveValue}\n"); + for (int i = 0; i < numWorkers; ++i) + { + for (int j = 0; j < numTasks; ++j) + { + // Test if x[i, j] is 0 or 1 (with tolerance for floating point + // arithmetic). + if (x[i, j].SolutionValue() > 0.5) + { + Console.WriteLine($"Worker {i} assigned to task {j}. Cost: {costs[i, j]}"); + } + } + } + } else { + Console.WriteLine("No solution found."); + } + // [END print_solution] + } +} diff --git a/ortools/linear_solver/samples/AssignmentMip.csproj b/ortools/linear_solver/samples/AssignmentMip.csproj new file mode 100644 index 0000000000..1413a8fe3d --- /dev/null +++ b/ortools/linear_solver/samples/AssignmentMip.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.AssignmentMip + true + + + + full + true + true + + + + + + + diff --git a/ortools/linear_solver/samples/AssignmentMip.java b/ortools/linear_solver/samples/AssignmentMip.java new file mode 100644 index 0000000000..4e260d8181 --- /dev/null +++ b/ortools/linear_solver/samples/AssignmentMip.java @@ -0,0 +1,120 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// 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.linearsolver.samples; +// [START import] +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPVariable; +// [END import] + +/** MIP example that solves an assignment problem. */ +public class AssignmentMip { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) { + // Data + // [START data_model] + double[][] costs = { + {90, 80, 75, 70}, + {35, 85, 55, 65}, + {125, 95, 90, 95}, + {45, 110, 95, 115}, + {50, 100, 90, 100}, + }; + int numWorkers = costs.length; + int numTasks = costs[0].length; + // [END data_model] + + // Solver + // [START solver] + // Create the linear solver with the CBC backend. + MPSolver solver = new MPSolver( + "AssignmentMip", MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); + // [END solver] + + // Variables + // [START variables] + // x[i][j] is an array of 0-1 variables, which will be 1 + // if worker i is assigned to task j. + MPVariable[][] x = new MPVariable[numWorkers][numTasks]; + for (int i = 0; i < numWorkers; ++i) { + for (int j = 0; j < numTasks; ++j) { + x[i][j] = solver.makeIntVar(0, 1, ""); + } + } + // [END variables] + + // Constraints + // [START constraints] + // Each worker is assigned to at most one task. + for (int i = 0; i < numWorkers; ++i) { + MPConstraint constraint = solver.makeConstraint(0, 1, ""); + for (int j = 0; j < numTasks; ++j) { + constraint.setCoefficient(x[i][j], 1); + } + } + // Each task is assigned to exactly one worker. + for (int j = 0; j < numTasks; ++j) { + MPConstraint constraint = solver.makeConstraint(1, 1, ""); + for (int i = 0; i < numWorkers; ++i) { + constraint.setCoefficient(x[i][j], 1); + } + } + // [END constraints] + + // Objective + // [START objective] + MPObjective objective = solver.objective(); + for (int i = 0; i < numWorkers; ++i) { + for (int j = 0; j < numTasks; ++j) { + objective.setCoefficient(x[i][j], costs[i][j]); + } + } + objective.setMinimization(); + // [END objective] + + // Solve + // [START solve] + MPSolver.ResultStatus resultStatus = solver.solve(); + // [END solve] + + // Print solution. + // [START print_solution] + // Check that the problem has a feasible solution. + if (resultStatus == MPSolver.ResultStatus.OPTIMAL + || resultStatus == MPSolver.ResultStatus.FEASIBLE) { + System.out.println("Total cost: " + objective.value() + "\n"); + for (int i = 0; i < numWorkers; ++i) { + for (int j = 0; j < numTasks; ++j) { + // Test if x[i][j] is 0 or 1 (with tolerance for floating point + // arithmetic). + if (x[i][j].solutionValue() > 0.5) { + System.out.println( + "Worker " + i + " assigned to task " + j + ". Cost = " + costs[i][j]); + } + } + } + } else { + System.err.println("No solution found."); + } + // [END print_solution] + } + + private AssignmentMip() {} +} +// [END program] diff --git a/ortools/linear_solver/samples/assignment_mip.cc b/ortools/linear_solver/samples/assignment_mip.cc new file mode 100644 index 0000000000..76340f9c76 --- /dev/null +++ b/ortools/linear_solver/samples/assignment_mip.cc @@ -0,0 +1,118 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// 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 + +#include "ortools/base/logging.h" +#include "ortools/linear_solver/linear_solver.h" +// [END import] + +namespace operations_research { +void IntegerProgrammingExample() { + // Data + // [START data_model] + const std::vector> costs{ + {90, 80, 75, 70}, {35, 85, 55, 65}, {125, 95, 90, 95}, + {45, 110, 95, 115}, {50, 100, 90, 100}, + }; + const int num_workers = costs.size(); + const int num_tasks = costs[0].size(); + // [END data_model] + + // Solver + // [START solver] + // Create the mip solver with the CBC backend. + MPSolver solver("simple_mip_program", + MPSolver::CBC_MIXED_INTEGER_PROGRAMMING); + // [END solver] + + // Variables + // [START variables] + // x[i][j] is an array of 0-1 variables, which will be 1 + // if worker i is assigned to task j. + std::vector> x( + num_workers, std::vector(num_tasks)); + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + x[i][j] = solver.MakeIntVar(0, 1, ""); + } + } + // [END variables] + + // Constraints + // [START constraints] + // Each worker is assigned to at most one task. + for (int i = 0; i < num_workers; ++i) { + LinearExpr worker_sum; + for (int j = 0; j < num_tasks; ++j) { + worker_sum += x[i][j]; + } + solver.MakeRowConstraint(worker_sum <= 1.0); + } + // Each task is assigned to exactly one worker. + for (int j = 0; j < num_tasks; ++j) { + LinearExpr task_sum; + for (int i = 0; i < num_workers; ++i) { + task_sum += x[i][j]; + } + solver.MakeRowConstraint(task_sum == 1.0); + } + // [END constraints] + + // Objective. + // [START objective] + MPObjective* const objective = solver.MutableObjective(); + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + objective->SetCoefficient(x[i][j], costs[i][j]); + } + } + objective->SetMinimization(); + // [END objective] + + // Solve + // [START solve] + const MPSolver::ResultStatus result_status = solver.Solve(); + // [END solve] + + // Print solution. + // [START print_solution] + // Check that the problem has a feasible solution. + if (result_status != MPSolver::OPTIMAL & + result_status != MPSolver::FEASIBLE) { + LOG(FATAL) << "No solution found."; + } + + LOG(INFO) << "Total cost = " << objective->Value() << "\n\n"; + + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + // Test if x[i][j] is 0 or 1 (with tolerance for floating point + // arithmetic). + if (x[i][j]->solution_value() > 0.5) { + LOG(INFO) << "Worker " << i << " assigned to task " << j + << ". Cost = " << costs[i][j]; + } + } + } + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::IntegerProgrammingExample(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/ortools/linear_solver/samples/assignment_mip.py b/ortools/linear_solver/samples/assignment_mip.py new file mode 100644 index 0000000000..667b7a665c --- /dev/null +++ b/ortools/linear_solver/samples/assignment_mip.py @@ -0,0 +1,91 @@ +# Copyright 2010-2018 Google LLC +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. +"""MIP example that solves an assignment problem.""" +# [START program] +# [START import] +from ortools.linear_solver import pywraplp +# [END import] + + +def main(): + # Data + # [START data_model] + costs = [ + [90, 80, 75, 70], + [35, 85, 55, 65], + [125, 95, 90, 95], + [45, 110, 95, 115], + [50, 100, 90, 100], + ] + num_workers = len(costs) + num_tasks = len(costs[0]) + # [END data_model] + + # Solver + # [START solver] + # Create the mip solver with the CBC backend. + solver = pywraplp.Solver('simple_mip_program', + pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING) + # [END solver] + + # Variables + # [START variables] + # x[i, j] is an array of 0-1 variables, which will be 1 + # if worker i is assigned to task j. + x = {} + for i in range(num_workers): + for j in range(num_tasks): + x[i, j] = solver.IntVar(0, 1, '') + # [END variables] + + # Constraints + # [START constraints] + # Each worker is assigned to at most 1 task. + for i in range(num_workers): + solver.Add(solver.Sum([x[i, j] for j in range(num_tasks)]) <= 1) + + # Each task is assigned to exactly one worker. + for j in range(num_tasks): + solver.Add(solver.Sum([x[i, j] for i in range(num_workers)]) == 1) + # [END constraints] + + # Objective + # [START objective] + objective_terms = [] + for i in range(num_workers): + for j in range(num_tasks): + objective_terms.append(costs[i][j] * x[i, j]) + solver.Minimize(solver.Sum(objective_terms)) + # [END objective] + + # Solve + # [START solve] + status = solver.Solve() + # [END solve] + + # Print solution. + # [START print_solution] + if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE: + print('Total cost = ', solver.Objective().Value(), '\n') + for i in range(num_workers): + for j in range(num_tasks): + # Test if x[i,j] is 1 (with tolerance for floating point arithmetic). + if x[i, j].solution_value() > 0.5: + print('Worker %d assigned to task %d. Cost = %d' % + (i, j, costs[i][j])) + # [END print_solution] + + +if __name__ == '__main__': + main() +# [END program]