sample: Rework MultipleKnapsack[Sat|Mip]

This commit is contained in:
Corentin Le Molgat
2021-11-25 11:53:16 +01:00
committed by Mizux Seiha
parent db96176998
commit e4da164368
8 changed files with 444 additions and 283 deletions

View File

@@ -17,7 +17,6 @@ using System;
using Google.OrTools.LinearSolver;
// [END import]
// [START program_part1]
public class MultipleKnapsackMip
{
// [START data_model]
@@ -30,57 +29,61 @@ public class MultipleKnapsackMip
public int NumBins = 5;
}
// [END data_model]
public static void Main()
{
// [START data]
DataModel data = new DataModel();
// [END data]
// [END program_part1]
// [START solver]
// Create the linear solver with the SCIP backend.
Solver solver = Solver.CreateSolver("SCIP");
// [END solver]
// [START program_part2]
// Variables
// [START variables]
Variable[,] x = new Variable[data.NumItems, data.NumBins];
for (int i = 0; i < data.NumItems; i++)
{
for (int j = 0; j < data.NumBins; j++)
for (int b = 0; b < data.NumBins; b++)
{
x[i, j] = solver.MakeIntVar(0, 1, $"x_{i}_{j}");
x[i, b] = solver.MakeBoolVar($"x_{i}_{b}");
}
}
// [END variables]
// Constraints
// [START constraints]
// Each item is assigned to at most one bin.
for (int i = 0; i < data.NumItems; ++i)
{
Constraint constraint = solver.MakeConstraint(0, 1, "");
for (int j = 0; j < data.NumBins; ++j)
for (int b = 0; b < data.NumBins; ++b)
{
constraint.SetCoefficient(x[i, j], 1);
constraint.SetCoefficient(x[i, b], 1);
}
}
for (int j = 0; j < data.NumBins; ++j)
// The amount packed in each bin cannot exceed its capacity.
for (int b = 0; b < data.NumBins; ++b)
{
Constraint constraint = solver.MakeConstraint(0, data.BinCapacities[j], "");
Constraint constraint = solver.MakeConstraint(0, data.BinCapacities[b], "");
for (int i = 0; i < data.NumItems; ++i)
{
constraint.SetCoefficient(x[i, j], DataModel.Weights[i]);
constraint.SetCoefficient(x[i, b], DataModel.Weights[i]);
}
}
// [END constraints]
// Objective
// [START objective]
Objective objective = solver.Objective();
for (int i = 0; i < data.NumItems; ++i)
{
for (int j = 0; j < data.NumBins; ++j)
for (int b = 0; b < data.NumBins; ++b)
{
objective.SetCoefficient(x[i, j], DataModel.Values[i]);
objective.SetCoefficient(x[i, b], DataModel.Values[i]);
}
}
objective.SetMaximization();
@@ -92,34 +95,35 @@ public class MultipleKnapsackMip
// [START print_solution]
// Check that the problem has an optimal solution.
if (resultStatus != Solver.ResultStatus.OPTIMAL)
if (resultStatus == Solver.ResultStatus.OPTIMAL)
{
Console.WriteLine($"Total packed value: {solver.Objective().Value()}");
double TotalWeight = 0.0;
for (int b = 0; b < data.NumBins; ++b)
{
double BinWeight = 0.0;
double BinValue = 0.0;
Console.WriteLine("Bin " + b);
for (int i = 0; i < data.NumItems; ++i)
{
if (x[i, b].SolutionValue() == 1)
{
Console.WriteLine($"Item {i} weight: {DataModel.Weights[i]} values: {DataModel.Values[i]}");
BinWeight += DataModel.Weights[i];
BinValue += DataModel.Values[i];
}
}
Console.WriteLine("Packed bin weight: " + BinWeight);
Console.WriteLine("Packed bin value: " + BinValue);
TotalWeight += BinWeight;
}
Console.WriteLine("Total packed weight: " + TotalWeight);
}
else
{
Console.WriteLine("The problem does not have an optimal solution!");
return;
}
Console.WriteLine("Total packed value: " + solver.Objective().Value());
double TotalWeight = 0.0;
for (int j = 0; j < data.NumBins; ++j)
{
double BinWeight = 0.0;
double BinValue = 0.0;
Console.WriteLine("Bin " + j);
for (int i = 0; i < data.NumItems; ++i)
{
if (x[i, j].SolutionValue() == 1)
{
Console.WriteLine($"Item {i} weight: {DataModel.Weights[i]} values: {DataModel.Values[i]}");
BinWeight += DataModel.Weights[i];
BinValue += DataModel.Values[i];
}
}
Console.WriteLine("Packed bin weight: " + BinWeight);
Console.WriteLine("Packed bin value: " + BinValue);
TotalWeight += BinWeight;
}
Console.WriteLine("Total packed weight: " + TotalWeight);
// [END print_solution]
}
}
// [END program_part2]
// [END program]

View File

@@ -11,8 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// MIP example that solves a multiple knapsack problem.
// [START program]
// MIP example that solves a multiple knapsack problem.
package com.google.ortools.linearsolver.samples;
// [START import]
import com.google.ortools.Loader;
@@ -24,7 +24,6 @@ import com.google.ortools.linearsolver.MPVariable;
/** Multiple knapsack problem. */
public class MultipleKnapsackMip {
// [START program_part1]
// [START data_model]
static class DataModel {
public final double[] weights = {48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36};
@@ -40,7 +39,6 @@ public class MultipleKnapsackMip {
// [START data]
final DataModel data = new DataModel();
// [END data]
// [END program_part1]
// [START solver]
// Create the linear solver with the SCIP backend.
@@ -51,36 +49,41 @@ public class MultipleKnapsackMip {
}
// [END solver]
// [START program_part2]
// Variables.
// [START variables]
MPVariable[][] x = new MPVariable[data.numItems][data.numBins];
for (int i = 0; i < data.numItems; ++i) {
for (int j = 0; j < data.numBins; ++j) {
x[i][j] = solver.makeIntVar(0, 1, "");
for (int b = 0; b < data.numBins; ++b) {
x[i][b] = solver.makeBoolVar("x_" + i + "_" + b);
}
}
// [END variables]
// Constraints.
// [START constraints]
// Each item is assigned to at most one bin.
for (int i = 0; i < data.numItems; ++i) {
MPConstraint constraint = solver.makeConstraint(0, 1, "");
for (int j = 0; j < data.numBins; ++j) {
constraint.setCoefficient(x[i][j], 1);
for (int b = 0; b < data.numBins; ++b) {
constraint.setCoefficient(x[i][b], 1);
}
}
for (int j = 0; j < data.numBins; ++j) {
MPConstraint constraint = solver.makeConstraint(0, data.binCapacities[j], "");
// The amount packed in each bin cannot exceed its capacity.
for (int b = 0; b < data.numBins; ++b) {
MPConstraint constraint = solver.makeConstraint(0, data.binCapacities[b], "");
for (int i = 0; i < data.numItems; ++i) {
constraint.setCoefficient(x[i][j], data.weights[i]);
constraint.setCoefficient(x[i][b], data.weights[i]);
}
}
// [END constraints]
// Objective.
// [START objective]
MPObjective objective = solver.objective();
for (int i = 0; i < data.numItems; ++i) {
for (int j = 0; j < data.numBins; ++j) {
objective.setCoefficient(x[i][j], data.values[i]);
for (int b = 0; b < data.numBins; ++b) {
objective.setCoefficient(x[i][b], data.values[i]);
}
}
objective.setMaximization();
@@ -93,22 +96,22 @@ public class MultipleKnapsackMip {
// [START print_solution]
// Check that the problem has an optimal solution.
if (resultStatus == MPSolver.ResultStatus.OPTIMAL) {
System.out.println("Total packed value: " + objective.value() + "\n");
System.out.println("Total packed value: " + objective.value());
double totalWeight = 0;
for (int j = 0; j < data.numBins; ++j) {
for (int b = 0; b < data.numBins; ++b) {
double binWeight = 0;
double binValue = 0;
System.out.println("Bin " + j + "\n");
System.out.println("Bin " + b);
for (int i = 0; i < data.numItems; ++i) {
if (x[i][j].solutionValue() == 1) {
if (x[i][b].solutionValue() == 1) {
System.out.println(
"Item " + i + " - weight: " + data.weights[i] + " value: " + data.values[i]);
"Item " + i + " weight: " + data.weights[i] + " value: " + data.values[i]);
binWeight += data.weights[i];
binValue += data.values[i];
}
}
System.out.println("Packed bin weight: " + binWeight);
System.out.println("Packed bin value: " + binValue + "\n");
System.out.println("Packed bin value: " + binValue);
totalWeight += binWeight;
}
System.out.println("Total packed weight: " + totalWeight);
@@ -120,5 +123,4 @@ public class MultipleKnapsackMip {
private MultipleKnapsackMip() {}
}
// [END program_part2]
// [END program]

View File

@@ -12,38 +12,37 @@
// limitations under the License.
// [START program]
// Solve a multiple knapsack problem using a MIP solver.
// [START import]
#include <iostream>
#include <numeric>
#include <vector>
#include "absl/strings/str_format.h"
#include "ortools/linear_solver/linear_expr.h"
#include "ortools/linear_solver/linear_solver.h"
// [END import]
// [START program_part1]
namespace operations_research {
// [START data_model]
struct DataModel {
const std::vector<int> weights = {48, 30, 42, 36, 36, 48, 42, 42,
36, 24, 30, 30, 42, 36, 36};
const std::vector<int> values = {10, 30, 25, 50, 35, 30, 15, 40,
30, 35, 45, 10, 20, 30, 25};
const int num_items = weights.size();
const int total_value = std::accumulate(values.begin(), values.end(), 0);
const std::vector<int> bin_capacities = {{100, 100, 100, 100, 100}};
const int num_bins = 5;
};
// [END data_model]
void MultipleKnapsackMip() {
// [START data]
DataModel data;
// [END data]
// [END program_part1]
const std::vector<int> weights = {
{48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36}};
const std::vector<int> values = {
{10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25}};
const int num_items = weights.size();
std::vector<int> all_items(num_items);
std::iota(all_items.begin(), all_items.end(), 0);
const std::vector<int> bin_capacities = {{100, 100, 100, 100, 100}};
const int num_bins = bin_capacities.size();
std::vector<int> all_bins(num_bins);
std::iota(all_bins.begin(), all_bins.end(), 0);
// [END data]
// [START solver]
// Create the mip solver with the SCIP backend.
// [START solver]
std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver("SCIP"));
if (!solver) {
LOG(WARNING) << "SCIP solver unavailable.";
@@ -51,48 +50,51 @@ void MultipleKnapsackMip() {
}
// [END solver]
// [START program_part2]
// Variables.
// [START variables]
// x[i][b] = 1 if item i is packed in bin b.
std::vector<std::vector<const MPVariable*>> x(
data.num_items, std::vector<const MPVariable*>(data.num_bins));
for (int i = 0; i < data.num_items; ++i) {
for (int j = 0; j < data.num_bins; ++j) {
x[i][j] = solver->MakeIntVar(0.0, 1.0, "");
num_items, std::vector<const MPVariable*>(num_bins));
for (int i : all_items) {
for (int b : all_bins) {
x[i][b] = solver->MakeBoolVar(
absl::StrFormat("x_%d_%d", i, b));
}
}
// [END variables]
// Constraints.
// [START constraints]
// Create the constraints.
// Each item is in at most one bin.
for (int i = 0; i < data.num_items; ++i) {
// Each item is assigned to at most one bin.
for (int i : all_items) {
LinearExpr sum;
for (int j = 0; j < data.num_bins; ++j) {
sum += x[i][j];
for (int b : all_bins) {
sum += x[i][b];
}
solver->MakeRowConstraint(sum <= 1.0);
}
// For each bin that is used, the total packed weight can be at most
// the bin capacity.
for (int j = 0; j < data.num_bins; ++j) {
LinearExpr weight;
for (int i = 0; i < data.num_items; ++i) {
weight += data.weights[i] * LinearExpr(x[i][j]);
// The amount packed in each bin cannot exceed its capacity.
for (int b : all_bins) {
LinearExpr bin_weight;
for (int i : all_items) {
bin_weight += LinearExpr(x[i][b]) * weights[i];
}
solver->MakeRowConstraint(weight <= data.bin_capacities[j]);
solver->MakeRowConstraint(bin_weight <= bin_capacities[b]);
}
// [END constraints]
// Objective.
// [START objective]
// Create the objective function.
// Maximize total value of packed items.
MPObjective* const objective = solver->MutableObjective();
LinearExpr value;
for (int i = 0; i < data.num_items; ++i) {
for (int j = 0; j < data.num_bins; ++j) {
value += data.values[i] * LinearExpr(x[i][j]);
LinearExpr objective_value;
for (int i : all_items) {
for (int b : all_bins) {
objective_value += LinearExpr(x[i][b]) * values[i];
}
}
objective->MaximizeLinearExpr(value);
objective->MaximizeLinearExpr(objective_value);
// [END objective]
// [START solve]
@@ -100,29 +102,30 @@ void MultipleKnapsackMip() {
// [END solve]
// [START print_solution]
// Check that the problem has an optimal solution.
if (result_status != MPSolver::OPTIMAL) {
std::cerr << "The problem does not have an optimal solution!";
}
std::cout << "Total packed value: " << objective->Value() << "\n\n";
double total_weight = 0;
for (int j = 0; j < data.num_bins; ++j) {
double bin_weight = 0;
double bin_value = 0;
std::cout << "Bin " << j << std::endl << std::endl;
for (int i = 0; i < data.num_items; ++i) {
if (x[i][j]->solution_value() == 1) {
std::cout << "Item " << i << " - weight: " << data.weights[i]
<< " value: " << data.values[i] << std::endl;
bin_weight += data.weights[i];
bin_value += data.values[i];
if (result_status == MPSolver::OPTIMAL) {
LOG(INFO) << "Total packed value: " << objective->Value();
double total_weight = 0.0;
for (int b : all_bins) {
LOG(INFO) << "Bin " << b;
double bin_weight = 0.0;
double bin_value = 0.0;
for (int i : all_items) {
if (x[i][b]->solution_value() > 0) {
LOG(INFO) << "Item " << i
<< " weight: " << weights[i]
<< " value: " << values[i];
bin_weight += weights[i];
bin_value += values[i];
}
}
LOG(INFO) << "Packed bin weight: " << bin_weight;
LOG(INFO) << "Packed bin value: " << bin_value;
total_weight += bin_weight;
}
std::cout << "Packed bin weight: " << bin_weight << std::endl;
std::cout << "Packed bin value: " << bin_value << std::endl << std::endl;
total_weight += bin_weight;
LOG(INFO) << "Total packed weight: " << total_weight;
} else {
LOG(INFO) << "The problem does not have an optimal solution.";
}
std::cout << "Total packed weight: " << total_weight << std::endl;
// [END print_solution]
}
} // namespace operations_research
@@ -131,5 +134,4 @@ int main(int argc, char** argv) {
operations_research::MultipleKnapsackMip();
return EXIT_SUCCESS;
}
// [END program_part2]
// [END program]

View File

@@ -22,41 +22,18 @@ from ortools.linear_solver import pywraplp
def create_data_model():
"""Create the data for the example."""
data = {}
weights = [48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36]
values = [10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25]
data['weights'] = weights
data['values'] = values
data['num_items'] = len(weights)
data['weights'] = [48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36]
data['values'] = [10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25]
assert len(data['weights']) == len(data['values'])
data['num_items'] = len(data['weights'])
data['all_items'] = range(data['num_items'])
data['bin_capacities'] = [100, 100, 100, 100, 100]
data['num_bins'] = len(data['bin_capacities'])
data['all_bins'] = range(data['num_bins'])
return data
# [END data_model]
# [START solution_printer]
def print_solutions(data, objective, x):
"""Display the solution."""
print('Total packed value:', objective.Value())
total_weight = 0
for b in data['all_bins']:
print(f'Bin {b}\n')
bin_weight = 0
bin_value = 0
for idx, weight in enumerate(data['weights']):
if x[idx, b].solution_value() > 0:
print(f"Item {idx} - weight: {weight} value: {data['values'][idx]}")
bin_weight += weight
bin_value += data['values'][idx]
print(f'Packed bin weight: {bin_weight}')
print(f'Packed bin value: {bin_value}\n')
total_weight += bin_weight
print(f'Total packed weight: {total_weight}')
# [END solution_printer]
def main():
# [START data]
data = create_data_model()
@@ -65,23 +42,25 @@ def main():
# Create the mip solver with the SCIP backend.
# [START solver]
solver = pywraplp.Solver.CreateSolver('SCIP')
if solver == None:
print('SCIP solver unavailable.')
return
# [END solver]
# Main variables.
# Variables.
# [START variables]
# Variables
# x[i, j] = 1 if item i is packed in bin j.
# x[i, b] = 1 if item i is packed in bin b.
x = {}
for idx in data['all_items']:
for i in data['all_items']:
for b in data['all_bins']:
x[idx, b] = solver.IntVar(0, 1, f'x_{idx}_{b}')
x[i, b] = solver.BoolVar(f'x_{i}_{b}')
# [END variables]
# Constraints
# Constraints.
# [START constraints]
# Each item can be in at most one bin.
for idx in data['all_items']:
solver.Add(sum(x[idx, b] for b in data['all_bins']) <= 1)
# Each item is assigned to at most one bin.
for i in data['all_items']:
solver.Add(sum(x[i, b] for b in data['all_bins']) <= 1)
# The amount packed in each bin cannot exceed its capacity.
for b in data['all_bins']:
@@ -90,7 +69,7 @@ def main():
for i in data['all_items']) <= data['bin_capacities'][b])
# [END constraints]
# Objective
# Objective.
# [START objective]
# Maximize total value of packed items.
objective = solver.Objective()
@@ -106,7 +85,21 @@ def main():
# [START print_solution]
if status == pywraplp.Solver.OPTIMAL:
print_solutions(data, objective, x)
print(f'Total packed value: {objective.Value()}')
total_weight = 0
for b in data['all_bins']:
print(f'Bin {b}')
bin_weight = 0
bin_value = 0
for i in data['all_items']:
if x[i, b].solution_value() > 0:
print(f"Item {i} weight: {data['weights'][i]} value: {data['values'][i]}")
bin_weight += data['weights'][i]
bin_value += data['values'][i]
print(f'Packed bin weight: {bin_weight}')
print(f'Packed bin value: {bin_value}')
total_weight += bin_weight
print(f'Total packed weight: {total_weight}')
else:
print('The problem does not have an optimal solution.')
# [END print_solution]

View File

@@ -0,0 +1,141 @@
// Copyright 2010-2021 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [START program]
// [START import]
using System;
using System.Collections.Generic;
using System.Linq;
using Google.OrTools.Sat;
// [START import]
public class MultipleKnapsackSat
{
public static void Main(String[] args)
{
// [START data]
long[] Weights = { 48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36 };
long[] Values = { 10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25 };
int NumItems = Weights.Length;
int[] allItems = Enumerable.Range(0, NumItems).ToArray();
long[] BinCapacities = { 100, 100, 100, 100, 100 };
int NumBins = BinCapacities.Length;
int[] allBins = Enumerable.Range(0, NumBins).ToArray();
// [END data]
// Model.
// [START model]
CpModel model = new CpModel();
// [END model]
// Variables
// [START variables]
IntVar[,] x = new IntVar[NumItems, NumBins];
for (int i = 0; i < NumItems; ++i)
{
for (int j = 0; j < NumBins; ++j)
{
x[i, j] = model.NewBoolVar($"x_{i}_{j}");
}
}
// [END variables]
// Constraints
// [START constraints]
// Each item is assigned to at most one bin.
for (int i = 0; i < NumItems; ++i)
{
IntVar[] vars = new IntVar[NumBins];
for (int b = 0; b < NumBins; ++b)
{
vars[b] = x[i, b];
}
model.Add(LinearExpr.Sum(vars) <= 1);
}
// The amount packed in each bin cannot exceed its capacity.
for (int b = 0; b < NumBins; ++b)
{
LinearExpr[] exprs = new LinearExpr[NumItems];
for (int i = 0; i < NumItems; ++i)
{
exprs[i] = LinearExpr.Affine(x[i, b], /*coeff=*/Weights[i], /*offset=*/0);
}
model.Add(LinearExpr.Sum(exprs) <= 1);
}
// [END constraints]
// Objective
// [START objective]
LinearExpr[] obj = new LinearExpr[NumItems * NumBins];
for (int i = 0; i < NumItems; ++i)
{
for (int b = 0; b < NumBins; ++b)
{
int k = i * NumBins + b;
obj[i] = LinearExpr.Affine(x[i, b], /*coeff=*/Values[i], /*offset=*/0);
}
}
model.Maximize(LinearExpr.Sum(obj));
// [END objective]
// Solve
// [START solve]
CpSolver solver = new CpSolver();
CpSolverStatus status = solver.Solve(model);
Console.WriteLine($"Solve status: {status}");
// [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 packed value: {solver.ObjectiveValue}");
double TotalWeight = 0.0;
for (int b = 0; b < NumBins; ++b)
{
double BinWeight = 0.0;
double BinValue = 0.0;
Console.WriteLine($"Bin {b}");
for (int i = 0; i < NumItems; ++i)
{
if (solver.Value(x[i, b]) == 1)
{
Console.WriteLine($"Item {i} weight: {Weights[i]} values: {Values[i]}");
BinWeight += Weights[i];
BinValue += Values[i];
}
}
Console.WriteLine("Packed bin weight: " + BinWeight);
Console.WriteLine("Packed bin value: " + BinValue);
TotalWeight += BinWeight;
}
Console.WriteLine("Total packed weight: " + TotalWeight);
}
else
{
Console.WriteLine("No solution found.");
}
// [END print_solution]
// [START statistics]
Console.WriteLine("Statistics");
Console.WriteLine($" conflicts: {solver.NumConflicts()}");
Console.WriteLine($" branches : {solver.NumBranches()}");
Console.WriteLine($" wall time: {solver.WallTime()}s");
// [END statistics]
}
}
// [END program]

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<LangVersion>7.3</LangVersion>
<TargetFramework>netcoreapp3.1</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
<!-- see https://github.com/dotnet/docs/issues/12237 -->
<RollForward>LatestMajor</RollForward>
<RestoreSources>../../../temp_dotnet/packages;$(RestoreSources);https://api.nuget.org/v3/index.json</RestoreSources>
<AssemblyName>Google.OrTools.MultipleKnapsackSat</AssemblyName>
<IsPackable>true</IsPackable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<GenerateTailCalls>true</GenerateTailCalls>
</PropertyGroup>
<ItemGroup>
<Compile Include="MultipleKnapsackSat.cs" />
<PackageReference Include="Google.OrTools" Version="9.1.*" />
</ItemGroup>
</Project>

View File

@@ -13,119 +13,94 @@
// [START program]
// [START import]
#include <map>
#include <numeric>
#include <tuple>
#include <vector>
#include "absl/strings/str_format.h"
#include "ortools/sat/cp_model.h"
// [END import]
namespace operations_research {
namespace sat {
// [START data_model]
struct DataModel {
const std::vector<int> weights = {{
48,
30,
42,
36,
36,
48,
42,
42,
36,
24,
30,
30,
42,
36,
36,
}};
const std::vector<int> values = {
{10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25}};
const int num_items = weights.size();
const int total_value = std::accumulate(values.begin(), values.end(), 0);
const std::vector<int> kBinCapacities = {{100, 100, 100, 100, 100}};
const int kNumBins = 5;
};
// [END data_model]
// [START solution_printer]
void PrintSolution(const DataModel data,
const std::vector<std::vector<IntVar>> x,
const std::vector<IntVar> load,
const std::vector<IntVar> value,
const CpSolverResponse response) {
for (int b = 0; b < data.kNumBins; ++b) {
LOG(INFO) << "Bin " << b;
LOG(INFO);
for (int i = 0; i < data.num_items; ++i) {
if (SolutionIntegerValue(response, x[i][b]) > 0) {
LOG(INFO) << "Item " << i << " - Weight: " << data.weights[i]
<< " Value: " << data.values[i];
}
}
LOG(INFO) << "Packed bin weight: "
<< SolutionIntegerValue(response, load[b]);
LOG(INFO) << "Packed bin value: "
<< SolutionIntegerValue(response, value[b]);
LOG(INFO);
}
LOG(INFO) << "Total packed weight: "
<< SolutionIntegerValue(response, LinearExpr::Sum(load));
LOG(INFO) << "Total packed value: "
<< SolutionIntegerValue(response, LinearExpr::Sum(value));
}
// [END solution_printer]
void MultipleKnapsackSat() {
// [START data]
DataModel data;
const std::vector<int> weights = {
{48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36}};
const std::vector<int> values = {
{10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25}};
const int total_value = std::accumulate(values.begin(), values.end(), 0);
const int num_items = weights.size();
std::vector<int> all_items(num_items);
std::iota(all_items.begin(), all_items.end(), 0);
const std::vector<int> bin_capacities = {{100, 100, 100, 100, 100}};
const int num_bins = bin_capacities.size();
std::vector<int> all_bins(num_bins);
std::iota(all_bins.begin(), all_bins.end(), 0);
// [END data]
// [START model]
CpModelBuilder cp_model;
// [END model]
// Variables.
// [START variables]
// Main variables.
std::vector<std::vector<IntVar>> x(data.num_items);
for (int i = 0; i < data.num_items; ++i) {
for (int b = 0; b < data.kNumBins; ++b) {
x[i].push_back(cp_model.NewIntVar({0, 1}));
// x[i, b] = 1 if item i is packed in bin b.
std::map<std::tuple<int, int>, IntVar> x;
for (int i : all_items) {
for (int b : all_bins) {
auto key = std::make_tuple(i, b);
x[key] = cp_model.NewBoolVar().WithName(
absl::StrFormat("x_%d_%d", i, b));
}
}
// Load variables.
std::vector<IntVar> load(data.kNumBins);
std::vector<IntVar> value(data.kNumBins);
for (int b = 0; b < data.kNumBins; ++b) {
load[b] = cp_model.NewIntVar({0, data.kBinCapacities[b]});
value[b] = cp_model.NewIntVar({0, data.total_value});
}
// Links load and x.
for (int b = 0; b < data.kNumBins; ++b) {
LinearExpr weightsExpr;
LinearExpr valuesExpr;
for (int i = 0; i < data.num_items; ++i) {
weightsExpr.AddTerm(x[i][b], data.weights[i]);
valuesExpr.AddTerm(x[i][b], data.values[i]);
}
cp_model.AddEquality(weightsExpr, load[b]);
cp_model.AddEquality(valuesExpr, value[b]);
std::vector<IntVar> load(num_bins);
std::vector<IntVar> value(num_bins);
for (int b : all_bins) {
load[b] = cp_model.NewIntVar({0, bin_capacities[b]});
value[b] = cp_model.NewIntVar({0, total_value});
}
// [END variables]
// Constraints.
// [START constraints]
// Each item can be in at most one bin.
for (int i = 0; i < data.num_items; ++i) {
// Each item is assigned to at most one bin.
for (int i : all_items) {
LinearExpr expr;
for (int b = 0; b < data.kNumBins; ++b) {
expr.AddTerm(x[i][b], 1);
for (int b : all_bins) {
auto key = std::make_tuple(i, b);
expr.AddTerm(x[key], 1);
}
cp_model.AddLessOrEqual(expr, 1);
}
// The amount packed in each bin cannot exceed its capacity.
for (int b : all_bins) {
LinearExpr bin_weight;
for (int i : all_items) {
auto key = std::make_tuple(i, b);
bin_weight.AddTerm(x[key], weights[i]);
}
cp_model.AddLessOrEqual(bin_weight, bin_capacities[b]);
}
// [END constraints]
// Maximize sum of load.
// Objective.
// [START objective]
cp_model.Maximize(LinearExpr::Sum(value));
// Maximize total value of packed items.
LinearExpr objective;
for (int i : all_items) {
for (int b : all_bins) {
auto key = std::make_tuple(i, b);
objective.AddTerm(x[key], values[i]);
}
}
cp_model.Maximize(objective);
// [END objective]
// [START solve]
@@ -133,16 +108,45 @@ void MultipleKnapsackSat() {
// [END solve]
// [START print_solution]
PrintSolution(data, x, load, value, response);
if (response.status() == CpSolverStatus::OPTIMAL ||
response.status() == CpSolverStatus::FEASIBLE) {
LOG(INFO) << "Total packed value: " << response.objective_value();
double total_weight = 0.0;
for (int b : all_bins) {
LOG(INFO) << "Bin " << b;
double bin_weight = 0.0;
double bin_value = 0.0;
for (int i : all_items) {
auto key = std::make_tuple(i, b);
if (SolutionIntegerValue(response, x[key]) > 0) {
LOG(INFO) << "Item " << i
<< " weight: " << weights[i]
<< " value: " << values[i];
bin_weight += weights[i];
bin_value += values[i];
}
}
LOG(INFO) << "Packed bin weight: " << bin_weight;
LOG(INFO) << "Packed bin value: " << bin_value;
total_weight += bin_weight;
}
LOG(INFO) << "Total packed weight: " << total_weight;
} else {
LOG(INFO) << "The problem does not have an optimal solution.";
}
// [END print_solution]
}
// Statistics.
// [START statistics]
LOG(INFO) << "Statistics";
LOG(INFO) << CpSolverResponseStats(response);
// [END statistics]
}
} // namespace sat
} // namespace operations_research
int main() {
operations_research::sat::MultipleKnapsackSat();
return EXIT_SUCCESS;
}
// [END program]

View File

@@ -13,7 +13,6 @@
# limitations under the License.
# [START program]
"""Solves a multiple knapsack problem using the CP-SAT solver."""
# [START import]
from ortools.sat.python import cp_model
# [END import]
@@ -23,45 +22,18 @@ from ortools.sat.python import cp_model
def create_data_model():
"""Create the data for the example."""
data = {}
weights = [48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36]
values = [10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25]
data['num_items'] = len(weights)
data['weights'] = [48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36]
data['values'] = [10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25]
assert len(data['weights']) == len(data['values'])
data['num_items'] = len(data['weights'])
data['all_items'] = range(data['num_items'])
data['weights'] = weights
data['values'] = values
data['bin_capacities'] = [100, 100, 100, 100, 100]
data['num_bins'] = len(data['bin_capacities'])
data['all_bins'] = range(data['num_bins'])
return data
# [END data_model]
# [START solution_printer]
def print_solutions(data, solver, x):
"""Display the solution."""
total_weight = 0
total_value = 0
for b in data['all_bins']:
print('Bin', b, '\n')
bin_weight = 0
bin_value = 0
for idx, val in enumerate(data['weights']):
if solver.Value(x[(idx, b)]) > 0:
print('Item', idx, '- Weight:', val, ' Value:',
data['values'][idx])
bin_weight += val
bin_value += data['values'][idx]
print('Packed bin weight:', bin_weight)
print('Packed bin value:', bin_value, '\n')
total_weight += bin_weight
total_value += bin_value
print('Total packed weight:', total_weight)
print('Total packed value:', total_value)
# [END solution_printer]
def main():
# [START data]
data = create_data_model()
@@ -71,12 +43,12 @@ def main():
model = cp_model.CpModel()
# [END model]
# Main variables.
# Variables.
# [START variables]
x = {}
for idx in data['all_items']:
for i in data['all_items']:
for b in data['all_bins']:
x[(idx, b)] = model.NewIntVar(0, 1, 'x_%i_%i' % (idx, b))
x[i, b] = model.NewBoolVar(f'x_{i}_{b}')
max_value = sum(data['values'])
# value[b] is the value of bin b when packed.
value = [
@@ -84,18 +56,19 @@ def main():
]
for b in data['all_bins']:
model.Add(value[b] == sum(
x[(i, b)] * data['values'][i] for i in data['all_items']))
x[i, b] * data['values'][i] for i in data['all_items']))
# [END variables]
# Constraints.
# [START constraints]
# Each item can be in at most one bin.
for idx in data['all_items']:
model.Add(sum(x[idx, b] for b in data['all_bins']) <= 1)
for i in data['all_items']:
model.Add(sum(x[i, b] for b in data['all_bins']) <= 1)
# The amount packed in each bin cannot exceed its capacity.
for b in data['all_bins']:
model.Add(
sum(x[(i, b)] * data['weights'][i]
sum(x[i, b] * data['weights'][i]
for i in data['all_items']) <= data['bin_capacities'][b])
# [END constraints]
@@ -114,7 +87,24 @@ def main():
# [START print_solution]
if status == cp_model.OPTIMAL:
print_solutions(data, solver, x)
total_weight = 0
total_value = 0
for b in data['all_bins']:
print('Bin', b, '\n')
bin_weight = 0
bin_value = 0
for idx, val in enumerate(data['weights']):
if solver.Value(x[(idx, b)]) > 0:
print('Item', idx, '- Weight:', val, ' Value:',
data['values'][idx])
bin_weight += val
bin_value += data['values'][idx]
print('Packed bin weight:', bin_weight)
print('Packed bin value:', bin_value, '\n')
total_weight += bin_weight
total_value += bin_value
print('Total packed weight:', total_weight)
print('Total packed value:', total_value)
# [END solutions_printer]