I have an optimization problem that I believe is a variant of the 'bin packing problem with precedence', but I'm unsure of if that is the correct paradigm to work with and I'm not having a huge amount of success searching for how to implement this problem and my desired constraints.
What I am trying to implement is this:
- I have 'x' input stacks of items which have two key parameters - color and length (see figure)
- I want to rearrange these input stacks of items into eight possible output locations - this number of locations/bins is fixed. I don't necessarily need to use all eight, but there is no possibility of more.
- My preference for placing items in locations/bins are:
- Colors grouped together
- Longer items cannot be stacked on top of shorter items
In addition:
- I can only access the top item of each input stack at any time
- I can only handle each item once
As noted, my first issue is understanding what type of problem this is so I can implement the appropriate framework.
From what I understand I have hard constraints (eight final locations, only access top item from each input stack) and soft constraints (group colors, no longer items on top of shorter items). Eventually I would like to add further soft constraints (e.g. preference taking items from input stack 1 first).
I have tried to repurpose an OR-Tools example to formulate my problem, but the output is not as expected and I'm not sure if it's because I'm going about this incorrectly, or I've just not formulated the problem/constraints properly.
from ortools.linear_solver import pywraplp
# Sample data representing the objects in the initial stacks
initial_stacks = [
[{'colour': 'red', 'length': '3'}, {'colour': 'blue', 'length': '4'}, {'colour': 'red', 'length': '3'}],
[{'colour': 'blue', 'length': '2'}, {'colour': 'green', 'length': '4'}, {'colour': 'red', 'length': '4'}],
[{'colour': 'green', 'length': '2'}, {'colour': 'red', 'length': '3'}, {'colour': 'blue', 'length': '3'}],
[{'colour': 'red', 'length': '4'}, {'colour': 'red', 'length': '3'}, {'colour': 'blue', 'length': '3'}]
]
num_initial_stacks = len(initial_stacks)
# Create the solver
solver = pywraplp.Solver.CreateSolver('SCIP')
# Create decision variables
num_new_stacks = 8
num_objects = sum(len(stack) for stack in initial_stacks)
num_colors = len(set(obj['colour'] for stack in initial_stacks for obj in stack))
x = {}
for i in range(num_objects):
for j in range(num_new_stacks):
for c in range(num_colors):
x[i, j, c] = solver.BoolVar(f'x[{i},{j},{c}]')
# Set constraints
# Each object is placed in only one stack and one length within each stack
for i in range(num_objects):
solver.Add(solver.Sum(x[i, j, c] for j in range(num_new_stacks) for c in range(num_colors)) == 1)
# Grouping constraints: Objects with the same colour are grouped together
for c in range(num_colors):
for j in range(num_new_stacks):
solver.Add(solver.Sum(x[i, j, c] for i in range(num_objects)) <= 1)
# Objective function: Maximize the number of grouped objects
objective = solver.Objective()
for i in range(num_objects):
for j in range(num_new_stacks):
for c in range(num_colors):
objective.SetCoefficient(x[i, j, c], 1)
objective.SetMaximization()
# Solve the problem
status = solver.Solve()
# Print the solution
if status == pywraplp.Solver.OPTIMAL:
print('Objective value:', objective.Value())
for j in range(num_new_stacks):
stack = []
for i in range(num_objects):
for c in range(num_colors):
if x[i, j, c].solution_value() == 1:
stack.append((f'Object {i}', f'Color {c}'))
print(f'New Stack {j+1}:', stack)
else:
print('The problem does not have an optimal solution.')
which produces the output:
Objective value: 12.0
New Stack 1: [('Object 0', 'Color 0'), ('Object 1', 'Color 1'), ('Object 2', 'Color 2')]
New Stack 2: [('Object 3', 'Color 0'), ('Object 4', 'Color 1'), ('Object 5', 'Color 2')]
New Stack 3: [('Object 6', 'Color 0'), ('Object 7', 'Color 1'), ('Object 8', 'Color 2')]
New Stack 4: [('Object 9', 'Color 0'), ('Object 10', 'Color 1'), ('Object 11', 'Color 2')]
New Stack 5: []
New Stack 6: []
New Stack 7: []
New Stack 8: []