Code Monkey home page Code Monkey logo

3d-bpp's Introduction

3D Bin Packing

This repository contains different implementations of heuristics-based and mathematical programming approaches to solve the 3D bin packing problem (3D-BPP):

  1. Baseline MP model [1]
  2. Maxrects generalized to 3D (2D implementation based on rectpack) [3]
  3. Column generation MP model [1]

All solutions are based on the concept of layers, which are collections of items having similar heights (within a pre-specified tolerance) that are thus having the same base z-coordinate. Using layers we are able to relax the 3D problem to a set of 2D ones, boosting efficiency-related metrics. Moreover, the usage of superitems (i.e. compounding similar items together in a bigger item) enables us to both lower the amount of items we are effectively dealing with and also to maximize layer density, thanks to the compact stacking of items in a superitem.

The bins building procedure is always handled in a procedural way, stacking layers on top of each other until a bin is full and opening new bins as needed. Before layers are placed in a bin, they are filtered based on a set of criteria (as described in [1]), such as item coverage and layer density. After layers are placed in a bin, some items could still be "flying" (i.e. have zero support at their base): to solve this issue, we let items "fall" to the ground as much as possible, without allowing intersections, thus ensuring compactness and correctness.

Dataset

The dataset in use is based on the benchmark dataset introduced in [1]. As the original code is not publicly available, we made our best effort to reproduce the dataset as closely as possible. A pre-computed dataset with 1M examples is available in the data/ folder (products.pkl).

Solutions

Below you can find a broad explanation of each implemented solution. Every procedure can be called through the same function (main() in module main.py), by changing the procedure parameter (it could be bl for baseline, mr for maxrects or cg for the column generation approach). Down below you can find a MWE on how to use the library:

from src import config, dataset, main

# Load dataset
product_dataset = dataset.ProductDataset(
    "data/products.pkl",
    config.NUM_PRODUCTS,
    config.MIN_PRODUCT_WIDTH,
    config.MAX_PRODUCT_WIDTH,
    config.MIN_PRODUCT_DEPTH,
    config.MAX_PRODUCT_DEPTH,
    config.MIN_PRODUCT_HEIGHT,
    config.MAX_PRODUCT_HEIGHT,
    config.MIN_PRODUCT_WEIGHT,
    config.MAX_PRODUCT_WEIGHT,
    force_overload=False,
)

# Get random order
order = product_dataset.get_order(50)

# Solve bin packing using the specified procedure to get
# a pool of bins without "flying" products
bin_pool = main.main(
    order,
    procedure="bl",
)

# Plot the bin pool
bin_pool.plot()

Baseline

The baseline model directly assigns superitems to layers and positions them by taking into account overlapment and layer height minimization. It reproduces model [SPPSI] of [1]. Beware that it might be very slow and we advice using it only for orders under 30 items.

Maxrects

Maxrects [3] is an heuristic algorithm that was built to solve the 2D bin packing problem. In our solution, maxrects is generalized to 3D thanks to the use of layers and height groups. In particular, items are grouped together based on their 3rd dimension (the height) and a specified tolerance. Such groups of items are then used as inputs for a custom maxrects procedure in which layers are thought of as bins. The maxrects algorithm itself is implemented in the rectpack library.

Column generation

The column generation solution aims to decompose the [SPPSI] approach in 2 components: the main and the pricing sub-problems. Column generation acts by iteratively solving both sub-problems ([RMP] and then [SP]) and incorporates more and more variables along the way. In this way, the main problem [RMP] selects the best layers so far and provides dual variables (one for each item) to be used in the pricing sub-problem [SP], which determines the best subset of items to compose a new layer, that will be carried forward (in the next iteration) iff its reduced cost is non-negative (otherwise column generation terminates). Moreover, column generation is given a set of columns as warm start and such layers are computed by either the maxrects procedure or by adding one layer for each item (each layer having only one item).



Authors in [1] further decompose the pricing sub-problem [SP] in two components: the no-placement and placement strategies. The former [SP-NP] serves as an item selection mechanism, thus ignoring the expensive placement constraints. The algorithm then checks whether there is a feasible placement of the selected items in a layer [SP-P]: if such a feasible placement exists, the column is added to the master problem, otherwise a new call to [SP-NP] is necessary, with an additional feasibility constraint (on the maximum number of selectable items). The no-placement/placement procedure continues until a feasible placement is found. In our implementation, we used a MIP solution for [SP-NP] (the sp_np_type parameter of the main.main function also allows to select a CP-based approach) and maxrects for [SP-P] (the sp_p_type parameter of the main.main function also allows to select exact MIP-based or CP-based approaches).

Differing from the author's description, our implementation does not incorporate column generation in a branch-and-price scheme.

Utilities

Lower bounds

Martello, Pisinger and Vigo [2] provided 3 different formulations to compute lower bounds on the number of required bins to pack the set of items we have at our disposal. In their paper, they present the following bounds:

  1. L0 (or continuous lower bound): comes from a relaxation in which each item's occupied space is associated with its liquid volume, thus ignoring the actual item placements and other constraints (it is shown to have a worst-case performance ratio of 1 / 8)
  2. L1: better suited for instances with a large number of items and still computable in linear time (its worst-case performance can be arbitrarily bad)
  3. L2: better suited for instances with a large number of items, but requires quadratic time to be computed (it is shown to have a worst-case performance ratio of 2 / 3)

To compute such lower bounds, you can rely on functions implemented in the utils module: get_l0_lb, get_l1_lb and get_l2_lb for L0, L1 and L2, respectively. All functions have the same interface: they require an order and a pallet_dims attribute. The former should be taken from the ProductDataset class using the get_order function, while the latter should be an instance of the Dimension class (available in the utils module). L0 returns a single value, while L1 and L2 return 4 values, with the first one being the actual lower bound and the other 3 values being lower bounds on 2 dimensions (as described in [2]).

Installation

In order to install all the dependecies required by the project, you can either rely on pip or conda. If you want to use the former, cd to the root folder of the project and run the following commands:

python3 -m venv venv
source venv/bin/activate
pip install -r init/requirements.txt

Instead, for the latter you'd have to run the following commands (after changing directory to the project's root):

conda env create --name 3d-bpp -f init/environment.yml
conda activate environment.yml

Execution

The library can be used from another project, by importing the necessary sources from src/ or it can be used as a standalone bin packing library, by leveraging the interface functions in src/main.py.

If you want to see a working example, please check out the bpp.ipynb Jupyter notebook, that contains an explanation of the dataset in use, along with a comparison of all the implemented solutions.

For a more entertaining and interactive solution, you can also run the implemented Streamlit dashboard, by simply running the following command from the root of the project:

python3 -m streamlit run src/dashboard.py

Slides

To start the slide show:

  1. Change directory to slides/
  2. Run npm install and npm run dev
  3. Visit http://localhost:3030

Edit the slides.md to see the changes.

Learn more about Slidev on documentations.

References

  • [1] Samir Elhedhli, Fatma Gzara and Burak Yildiz (2019),
    Three-Dimensional Bin Packing and Mixed-Case Palletization,
    INFORMS Journal on Optimization.
  • [2] Silvano Martello, David Pisinger and Daniele Vigo (1998),
    The Three-Dimensional Bin Packing Problem,
    Operations Research.
  • [3] Jukka Jylänki (2010).
    A Thousand Ways to Pack the Bin - A Practical Approach to Two-Dimensional Rectangle Bin Packing.
    -.

3d-bpp's People

Contributors

blancray avatar leocalbi avatar wadaboa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

3d-bpp's Issues

About future improvements

Hi,

first of all, thanks a lot for sharing.
Regarding the improved part you mentioned in your report, is it in the work plan in the near future?
I would like to know how to make the project support 3D rotation, can you give me some advices?

Thanks,
Zhe.

baseline.py TypeError: object of type 'generator' has no len()

Hi,
this is a very thankful job, u done.
I tried to play with it, but some error occurs.

In baseline.py, when reached the notebook section [35] the following error occurs: TypeError: object of type 'generator' has no len()

Conda installed python is 3.11 instead given in Dockerfile
ortools==8.2.8710 isn't installable

In every case of calling ortools 'cp_model.LinearExpr.Sum' function it returns this error.

Can You help to solve this?

Thanks

Related info

[35]
bl_bin_pool = main.main(bl_order, procedure="bl", tlim=20)
bl_bin_pool.get_original_layer_pool().to_dataframe()

2023-09-11 09:29:04.210 | INFO     | baseline:baseline:165 - Solving baseline model
2023-09-11 09:29:04.201 | INFO     | main:main:169 - BL procedure starting
2023-09-11 09:29:04.202 | INFO     | main:main:179 - BL iteration 1/1
2023-09-11 09:29:04.205 | DEBUG    | superitems:_gen_single_items_superitems:639 - Generated 20 superitems with a single item
2023-09-11 09:29:04.205 | INFO     | superitems:gen_superitems:623 - Generating horizontal superitems of type 'two-width'
2023-09-11 09:29:04.206 | DEBUG    | superitems:_gen_superitems_horizontal:685 - Generated 0 horizontal superitems with 2 items
2023-09-11 09:29:04.207 | DEBUG    | superitems:_gen_superitems_horizontal:692 - Generated 0 horizontal superitems with 4 items
2023-09-11 09:29:04.207 | INFO     | superitems:gen_superitems:626 - Generating vertical superitems with maximum stacking of 4
2023-09-11 09:29:04.208 | DEBUG    | superitems:_gen_superitems_vertical:770 - Generated 15 wide vertical superitems
2023-09-11 09:29:04.209 | DEBUG    | superitems:_gen_superitems_vertical:772 - Generated 0 deep vertical superitems
2023-09-11 09:29:04.209 | INFO     | superitems:gen_superitems:628 - Generated 35 superitems
2023-09-11 09:29:04.209 | INFO     | superitems:gen_superitems:630 - Remaining superitems after filtering by pallet dimensions: 35
2023-09-11 09:29:04.210 | INFO     | baseline:baseline:165 - Solving baseline model
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[35], line 1
----> 1 bl_bin_pool = main.main(bl_order, procedure="bl", tlim=20)
      2 bl_bin_pool.get_original_layer_pool().to_dataframe()

File ~/work/src/main.py:194, in main(order, procedure, max_iters, superitems_horizontal, superitems_horizontal_type, superitems_max_vstacked, density_tol, filtering_two_dims, filtering_max_coverage_all, filtering_max_coverage_single, tlim, enable_solver_output, height_tol, cg_use_height_groups, cg_mr_warm_start, cg_max_iters, cg_max_stag_iters, cg_sp_mr, cg_sp_np_type, cg_sp_p_type, cg_return_only_last)
    192 # Call the right packing procedure
    193 if procedure == "bl":
--> 194     layer_pool = baseline.baseline(superitems_pool, config.PALLET_DIMS, tlim=tlim)
    195 elif procedure == "mr":
    196     layer_pool = maxrects_warm_start(
    197         superitems_pool, height_tol=height_tol, density_tol=density_tol, add_single=False
    198     )

File ~/work/src/baseline.py:166, in baseline(superitems_pool, pallet_dims, tlim, num_workers)
    164 # Call the baseline model
    165 logger.info("Solving baseline model")
--> 166 sol, solve_time = baseline_model(
    167     fsi, ws, ds, hs, pallet_dims, tlim=tlim, num_workers=num_workers
    168 )
    169 logger.info(f"Solved baseline model in {solve_time:.2f} seconds")
    171 # Build the layer pool from the model's solution

File ~/work/src/baseline.py:62, in baseline_model(fsi, ws, ds, hs, pallet_dims, tlim, num_workers)
     58 # Constraints
     59 # Ensure that every item is included in exactly one layer
     60 for i in range(n_items):
     61     model.Add(
---> 62         cp_model.LinearExpr.Sum(
     63             fsi[s, i] * zsl[s, l] for s in range(n_superitems) for l in range(max_layers)
     64         )
     65         == 1
     66     )
     68 # Define the height of layer l
     69 for l in range(max_layers):

File /opt/conda/lib/python3.11/site-packages/ortools/sat/python/cp_model.py:183, in LinearExpr.Sum(cls, expressions)
    180 @classmethod
    181 def Sum(cls, expressions):
    182     """Creates the expression sum(expressions)."""
--> 183     if len(expressions) == 1:
    184         return expressions[0]
    185     return _SumArray(expressions)

TypeError: object of type 'generator' has no len()

I tried to convert it to a docker container with the following params:

  • ortools==9.5.2237 (original isn't reachable)
  • python 3.9.6
  • nb_black (installed from git --> 1.0.7 has restriction for python < 3.6)

The Dockerfile:

FROM jupyter/base-notebook

# Name your environment and choose the python version
ARG env_name=python3.9.6
ARG py_ver=3.9.6

COPY --chown=${NB_UID}:${NB_GID} /init/requirements.txt /tmp/
COPY --chown=${NB_UID}:${NB_GID} /init/environment.yml /tmp/
RUN mamba env create -p "${CONDA_DIR}/envs/${env_name}" -f /tmp/environment.yml && \
  mamba clean --all -f -y

# Create Python kernel and link it to jupyter
RUN "${CONDA_DIR}/envs/${env_name}/bin/python" -m ipykernel install --user --name="${env_name}" && \
  fix-permissions "${CONDA_DIR}" && \
  fix-permissions "/home/${NB_USER}"

RUN "${CONDA_DIR}/envs/${env_name}/bin/pip" install --no-cache-dir \
  'flake8'

USER root
RUN apt update -y
RUN apt install git -y

RUN pip install --no-cache-dir -r /tmp/requirements.txt
#USER ${NB_UID}
RUN pip install git+https://github.com/IsaGrue/nb_black.git

USER root
RUN activate_custom_env_script=/usr/local/bin/before-notebook.d/activate_custom_env.sh && \
  echo "#!/bin/bash" > ${activate_custom_env_script} && \
  echo "eval \"$(conda shell.bash activate "${env_name}")\"" >> ${activate_custom_env_script} && \
  chmod +x ${activate_custom_env_script}

USER ${NB_UID}

RUN echo "conda activate ${env_name}" >> "${HOME}/.bashrc"

The docker-compose.yaml

version: "3"

services:
  app:
    container_name: 3dp-packing
    build:
      context: .
      dockerfile: ./Dockerfile
    # command: flask --app ./src/hello --debug run --host=0.0.0.0 --port=8080
    image: 3dp-packing:latest
    volumes:
      - ${PWD}/work:/home/jovyan/work

    ports:
      - 8888:8888
      - 8787:8787

requirements.txt

numpy
pandas
ortools==9.5.2237
matplotlib
ipympl==0.7.0
rectpack
tqdm==4.60.0
scipy
seaborn
streamlit==1.8.1
watchdogs==1.8.2
loguru==0.5.3

environment.yaml

name: 3d-bpp

channels:
  - conda-forge
  - defaults

dependencies:
  - black==21.7b0
  - loguru==0.5.3
  - matplotlib==3.4.2
  - nb_black==1.0.7
  - numpy
  - pandas
  - pip==21.2.1
  - python==3.9.6
  - seaborn==0.11.2
  - tqdm==4.61.2
  - streamlit==0.85.1
  - pip:
      - ortools==9.5.2237
      - rectpack==0.2.2

utils.Dimension

When running this line of code:

bl_bin_pool = main.main(bl_order, procedure="bl", tlim=20)
bl_bin_pool.get_original_layer_pool().to_dataframe()

I get this error:

---> 18 self.dimensions = utils.Dimension(width, depth, height, weight)

AttributeError: module 'utils' has no attribute 'Dimension'

How can I solve it?

KeyError: 'o_0' in baselin.py

got a Keyerror when using the baseline algorithm

File c:\Users\g7morsj22l\Documents\02_Code_Repos\3d-bpp\src\baseline.py:174, in baseline(superitems_pool, pallet_dims, tlim, num_workers)
    172 layer_pool = layers.LayerPool(superitems_pool, pallet_dims)
    173 for l in range(max_layers):
--> 174     if sol[f"o_{l}"] > 0:
    175         superitems_in_layer = [s for s in range(n_superitems) if sol[f"z_{s}_{l}"] == 1]
    176         layer = utils.build_layer_from_model_output(
    177             superitems_pool, superitems_in_layer, sol, pallet_dims

Get boxes positions

How can I get from main a list of all the locations of the boxes placed in the container when I use maxrects

dashboard.py with error

Hi,
When I run dashboard.py, there has following error:

pyarrow.lib.ArrowInvalid: ("Could not convert 'Total' with type str: tried to convert to int64", 'Conversion failed for column layer with type object')

I think it may be caused by layer column value in bin.layer_pool.describe() is mixed which are int and string.
I transfer the layer value type:

 df = bin.layer_pool.describe()
 df["layer"] = df["layer"] .apply(str)

It's ok.

I'm not sure if it's a version problem that's causing these problems.
Anyway, this is the problem I encountered and the solution. I hope other people with the same problem can refer to it.

Thanks

online packing

Hi, I would like to ask, if it is possible to solve 3D online bin packing problem?
Or if anyone knows about any solution for 3D online bin packing problem, please, write :)

object of type 'generator' has no len()

Hi,
when I execute the bl_bin_pool = main.main(bl_order, procedure="bl", tlim=20), error appears:

TypeError Traceback (most recent call last)
Input In [24], in <cell line: 1>()
----> 1 bl_bin_pool = main.main(bl_order, procedure="bl", tlim=20)
2 bl_bin_pool.get_original_layer_pool().to_dataframe()

File ~/Documents/3d-bpp/src/main.py:194, in main(order, procedure, max_iters, superitems_horizontal, superitems_horizontal_type, superitems_max_vstacked, density_tol, filtering_two_dims, filtering_max_coverage_all, filtering_max_coverage_single, tlim, enable_solver_output, height_tol, cg_use_height_groups, cg_mr_warm_start, cg_max_iters, cg_max_stag_iters, cg_sp_mr, cg_sp_np_type, cg_sp_p_type, cg_return_only_last)
192 # Call the right packing procedure
193 if procedure == "bl":
--> 194 layer_pool = baseline.baseline(superitems_pool, config.PALLET_DIMS, tlim=tlim)
195 elif procedure == "mr":
196 layer_pool = maxrects_warm_start(
197 superitems_pool, height_tol=height_tol, density_tol=density_tol, add_single=False
198 )

File ~/Documents/3d-bpp/src/baseline.py:166, in baseline(superitems_pool, pallet_dims, tlim, num_workers)
164 # Call the baseline model
165 logger.info("Solving baseline model")
--> 166 sol, solve_time = baseline_model(
167 fsi, ws, ds, hs, pallet_dims, tlim=tlim, num_workers=num_workers
168 )
169 logger.info(f"Solved baseline model in {solve_time:.2f} seconds")

File ~/Documents/3d-bpp/src/baseline.py:62, in baseline_model(fsi, ws, ds, hs, pallet_dims, tlim, num_workers)
58 # Constraints
59 # Ensure that every item is included in exactly one layer
60 for i in range(n_items):
61 model.Add(
---> 62 cp_model.LinearExpr.Sum(
63 fsi[s, i] * zsl[s, l] for s in range(n_superitems) for l in range(max_layers)
64 )
65 == 1
66 )
68 # Define the height of layer l
69 for l in range(max_layers):

File ~/Documents/nguyens-RL/nguyens-RL/lib/python3.9/site-packages/ortools/sat/python/cp_model.py:182, in LinearExpr.Sum(cls, expressions)
179 @classmethod
180 def Sum(cls, expressions):
181 """Creates the expression sum(expressions)."""
--> 182 if len(expressions) == 1:
183 return expressions[0]
184 return _SumArray(expressions)

How can I use this project in container loading?

Hi,
If I want to use this project in container loading,

for example, I hvae 4 containers with (2280,2550,12000) size (width,height,length) , I want to loading 24 small boxes with some sizes and these boxes may be loaded 3 or 4 containers.

I found the "cg" and "bl" only support only one container loading, how can I solve such cases?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.