81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright 2010-2024 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.
|
|
|
|
"""Solves a binpacking problem using the CP-SAT solver."""
|
|
|
|
|
|
from ortools.sat.python import cp_model
|
|
|
|
|
|
def binpacking_problem_sat():
|
|
"""Solves a bin-packing problem using the CP-SAT solver."""
|
|
# Data.
|
|
bin_capacity = 100
|
|
slack_capacity = 20
|
|
num_bins = 5
|
|
all_bins = range(num_bins)
|
|
|
|
items = [(20, 6), (15, 6), (30, 4), (45, 3)]
|
|
num_items = len(items)
|
|
all_items = range(num_items)
|
|
|
|
# Model.
|
|
model = cp_model.CpModel()
|
|
|
|
# Main variables.
|
|
x = {}
|
|
for i in all_items:
|
|
num_copies = items[i][1]
|
|
for b in all_bins:
|
|
x[(i, b)] = model.new_int_var(0, num_copies, f"x[{i},{b}]")
|
|
|
|
# Load variables.
|
|
load = [model.new_int_var(0, bin_capacity, f"load[{b}]") for b in all_bins]
|
|
|
|
# Slack variables.
|
|
slacks = [model.new_bool_var(f"slack[{b}]") for b in all_bins]
|
|
|
|
# Links load and x.
|
|
for b in all_bins:
|
|
model.add(load[b] == sum(x[(i, b)] * items[i][0] for i in all_items))
|
|
|
|
# Place all items.
|
|
for i in all_items:
|
|
model.add(sum(x[(i, b)] for b in all_bins) == items[i][1])
|
|
|
|
# Links load and slack through an equivalence relation.
|
|
safe_capacity = bin_capacity - slack_capacity
|
|
for b in all_bins:
|
|
# slack[b] => load[b] <= safe_capacity.
|
|
model.add(load[b] <= safe_capacity).only_enforce_if(slacks[b])
|
|
# not(slack[b]) => load[b] > safe_capacity.
|
|
model.add(load[b] > safe_capacity).only_enforce_if(~slacks[b])
|
|
|
|
# Maximize sum of slacks.
|
|
model.maximize(sum(slacks))
|
|
|
|
# Solves and prints out the solution.
|
|
solver = cp_model.CpSolver()
|
|
status = solver.solve(model)
|
|
print(f"solve status: {solver.status_name(status)}")
|
|
if status == cp_model.OPTIMAL:
|
|
print(f"Optimal objective value: {solver.objective_value}")
|
|
print("Statistics")
|
|
print(f" - conflicts : {solver.num_conflicts}")
|
|
print(f" - branches : {solver.num_branches}")
|
|
print(f" - wall time : {solver.wall_time}s")
|
|
|
|
|
|
binpacking_problem_sat()
|