diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index ecc9989da6..b55c096986 100644 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -370,6 +370,7 @@ test_cc_constraint_solver_samples: \ .PHONY: test_cc_sat_samples # Build and Run all C++ Sat Samples (located in ortools/sat/samples) test_cc_sat_samples: \ + rcc_assignment_sat \ rcc_binpacking_problem_sat \ rcc_bool_or_sample_sat \ rcc_channeling_sample_sat \ diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index 3f62b70b67..bf78479548 100644 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -471,6 +471,7 @@ test_java_linear_solver_samples: \ .PHONY: test_java_sat_samples # Build and Run all Java SAT Samples (located in ortools/sat/samples) test_java_sat_samples: \ + rjava_AssignmentSat \ rjava_BinPackingProblemSat \ rjava_BoolOrSampleSat \ rjava_ChannelingSampleSat \ diff --git a/makefiles/Makefile.python.mk b/makefiles/Makefile.python.mk index dbcfa92105..a3030c6200 100644 --- a/makefiles/Makefile.python.mk +++ b/makefiles/Makefile.python.mk @@ -602,6 +602,7 @@ test_python_linear_solver_samples: \ .PHONY: test_python_sat_samples # Run all Python Sat Samples (located in ortools/sat/samples) test_python_sat_samples: \ + rpy_assignment_sat \ rpy_binpacking_problem_sat \ rpy_bool_or_sample_sat \ rpy_channeling_sample_sat \ diff --git a/ortools/sat/samples/AssignmentSat.java b/ortools/sat/samples/AssignmentSat.java new file mode 100644 index 0000000000..0e52f3330b --- /dev/null +++ b/ortools/sat/samples/AssignmentSat.java @@ -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. + +// CP-SAT example that solves an assignment problem. +// [START program] +package com.google.ortools.sat.samples; +// [START import] +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; +// [END import] + +/** Assignment problem. */ +public class AssignmentSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) { + // 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}, + }; + final int numWorkers = costs.length; + final int numTasks = costs[0].length; + // [END data_model] + + // Model + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Variables + // [START variables] + IntVar[][] x = new IntVar[numWorkers][numTasks]; + // Variables in a 1-dim array. + IntVar[] xFlat = new IntVar[numWorkers * numTasks]; + int[] costsFlat = new int[numWorkers * numTasks]; + for (int i = 0; i < numWorkers; ++i) { + for (int j = 0; j < numTasks; ++j) { + x[i][j] = model.newIntVar(0, 1, ""); + int k = i * numTasks + j; + xFlat[k] = x[i][j]; + costsFlat[k] = costs[i][j]; + } + } + // [END variables] + + // Constraints + // [START constraints] + // Each worker is assigned to at most one task. + for (int i = 0; i < numWorkers; ++i) { + IntVar[] vars = new IntVar[numTasks]; + for (int j = 0; j < numTasks; ++j) { + vars[j] = x[i][j]; + } + model.addLessOrEqual(LinearExpr.sum(vars), 1); + } + // Each task is assigned to exactly one worker. + for (int j = 0; j < numTasks; ++j) { + // LinearExpr taskSum; + IntVar[] vars = new IntVar[numWorkers]; + for (int i = 0; i < numWorkers; ++i) { + vars[i] = x[i][j]; + } + model.addEquality(LinearExpr.sum(vars), 1); + } + // [END constraints] + + // Objective + // [START objective] + model.minimize(LinearExpr.scalProd(xFlat, costsFlat)); + // [END objective] + + // Solve + // [START solve] + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + // [END solve] + + // Print solution. + // [START print_solution] + // Check that the problem has a feasible solution. + if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) { + System.out.println("Total cost: " + solver.objectiveValue() + "\n"); + for (int i = 0; i < numWorkers; ++i) { + for (int j = 0; j < numTasks; ++j) { + if (solver.value(x[i][j]) == 1) { + System.out.println( + "Worker " + i + " assigned to task " + j + ". Cost: " + costs[i][j]); + } + } + } + } else { + System.err.println("No solution found."); + } + // [END print_solution] + } + + private AssignmentSat() {} +} diff --git a/ortools/sat/samples/assignment_sat.cc b/ortools/sat/samples/assignment_sat.cc new file mode 100644 index 0000000000..e5c640b8ec --- /dev/null +++ b/ortools/sat/samples/assignment_sat.cc @@ -0,0 +1,110 @@ +// 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 "ortools/sat/cp_model.h" +// [END import] +namespace operations_research { +namespace sat { + +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] + + // Model + // [START model] + CpModelBuilder cp_model; + // [END model] + + // Variables + // [START variables] + // x[i][j] is an array of Boolean variables. x[i][j] is true + // 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] = cp_model.NewBoolVar(); + } + } + // [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.AddTerm(x[i][j], 1); + } + cp_model.AddLessOrEqual(worker_sum, 1); + } + // 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.AddTerm(x[i][j], 1); + } + cp_model.AddEquality(task_sum, 1); + } + // [END constraints] + + // Objective + // [START objective] + LinearExpr total_cost; + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + total_cost.AddTerm(x[i][j], costs[i][j]); + } + } + cp_model.Minimize(total_cost); + // [END objective] + + // Solve + // [START solve] + const CpSolverResponse response = Solve(cp_model.Build()); + // [END solve] + + // Print solution. + // [START print_solution] + if (response.status() == CpSolverStatus::INFEASIBLE) { + LOG(FATAL) << "No solution found."; + } + + LOG(INFO) << "Total cost: " << response.objective_value(); + LOG(INFO); + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + if (SolutionBooleanValue(response, x[i][j])) { + LOG(INFO) << "Task " << i << " assigned to worker " << j + << ". Cost: " << costs[i][j]; + } + } + } + // [END print_solution] +} +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::sat::IntegerProgrammingExample(); + return EXIT_SUCCESS; +} diff --git a/ortools/sat/samples/assignment_sat.py b/ortools/sat/samples/assignment_sat.py new file mode 100644 index 0000000000..5b3290d3f2 --- /dev/null +++ b/ortools/sat/samples/assignment_sat.py @@ -0,0 +1,92 @@ +# 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. +"""Solve a simple assignment problem.""" +# [START program] +# [START import] +from ortools.sat.python import cp_model +# [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] + + # Model + # [START model] + model = cp_model.CpModel() + # [END model] + + # Variables + # [START variables] + x = [] + for i in range(num_workers): + t = [] + for j in range(num_tasks): + t.append(model.NewBoolVar('x[%i,%i]' % (i, j))) + x.append(t) + # [END variables] + + # Constraints + # [START constraints] + # Each worker is assigned to at most one task. + for i in range(num_workers): + model.Add(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): + model.Add(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]) + model.Minimize(sum(objective_terms)) + # [END objective] + + # Solve + # [START solve] + solver = cp_model.CpSolver() + status = solver.Solve(model) + # [END solve] + + # Print solution. + # [START print_solution] + if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: + print('Total cost = %i' % solver.ObjectiveValue()) + print() + for i in range(num_workers): + for j in range(num_tasks): + if solver.BooleanValue(x[i][j]): + print('Worker ', i, ' assigned to task ', j, ' Cost = ', + costs[i][j]) + else: + print('No solution found.') + # [END print_solution] + + +if __name__ == '__main__': + main() +# [END program]