Skip to main content

U-Nesting

A high-performance 2D/3D spatial optimization engine for nesting and bin packing problems.

PyPI version License: MIT

Features

  • 2D Nesting: Optimal placement of irregular polygons on sheets
  • 3D Bin Packing: Efficient box placement in containers
  • Multiple Algorithms: BLF, NFP-guided, Genetic Algorithm, BRKGA, Simulated Annealing
  • High Performance: Written in Rust with Python bindings
  • Type Hints: Full type annotation support

Installation

pip install u-nesting

Quick Start

2D Nesting

import u_nesting

# Define polygons to nest
geometries = [
    {
        "id": "part1",
        "polygon": [[0, 0], [100, 0], [100, 50], [0, 50]],
        "quantity": 5,
        "rotations": [0, 90, 180, 270]
    },
    {
        "id": "triangle",
        "polygon": [[0, 0], [80, 0], [40, 60]],
        "quantity": 3
    }
]

# Define sheet boundary
boundary = {"width": 500, "height": 300}

# Configure solver
config = {
    "strategy": "nfp",      # Options: blf, nfp, ga, brkga, sa, gdrr, alns
    "spacing": 2.0,         # Gap between parts
    "time_limit_ms": 30000  # 30 second timeout
}

# Solve
result = u_nesting.solve_2d(geometries, boundary, config)

print(f"Utilization: {result['utilization']:.1%}")
print(f"Placed: {len(result['placements'])} items")
for p in result['placements']:
    print(f"  {p['geometry_id']}[{p['instance']}]: ({p['position'][0]:.1f}, {p['position'][1]:.1f})")

3D Bin Packing

import u_nesting

# Define boxes to pack
geometries = [
    {
        "id": "small",
        "dimensions": [20, 20, 20],
        "quantity": 10
    },
    {
        "id": "large",
        "dimensions": [40, 30, 25],
        "quantity": 5,
        "mass": 2.5  # Optional weight
    }
]

# Define container
boundary = {
    "dimensions": [200, 150, 100],
    "max_mass": 50.0,    # Optional mass limit
    "gravity": True,     # Stack from bottom
    "stability": True    # Require stable placement
}

# Configure solver
config = {
    "strategy": "ep",       # Extreme Point heuristic
    "time_limit_ms": 10000
}

# Solve
result = u_nesting.solve_3d(geometries, boundary, config)

print(f"Utilization: {result['utilization']:.1%}")
print(f"Containers used: {result['boundaries_used']}")

API Reference

solve_2d(geometries, boundary, config=None) -> dict

Solve a 2D nesting problem.

Parameters:

Keys are validated strictly: an unrecognized key in geometries, boundary or config raises ValueError naming it, rather than being ignored. A typo can therefore never silently fall back to a default. So does a value of the wrong type -- a count given as a fraction (2.5) included. A count outside its range (a negative one included) is a range refusal: a geometry's quantity below 1 comes back as code parameter_out_of_range with details parameter geometries[i].quantity and the geometry's id; a config count (population_size below 2, seed below 0, ...) raises ValueError naming the setting and its range.

What the solver refuses comes back in the result dict (success: False, code, details): an empty geometries list is empty_input (parameter geometries); a boundary side, dimension or max_mass of 0 or below is parameter_out_of_range naming it (boundary.width, boundary.height, boundary.dimensions[i], boundary.max_mass, with exclusive_min 0), and a setting is named by its path in config -- config.strategy for a strategy the dimension does not offer. A boundary given as both a rectangle and a polygon, or half of a rectangle, raises ValueError.

  • geometries: List of geometry definitions
    • id (str): Unique identifier
    • polygon (list): Vertices as [[x, y], ...]
    • quantity (int): Number of copies (default: 1)
    • rotations (list): Allowed rotation angles in degrees — at least one; omit it for a fixed orientation (an empty list is refused)
    • allow_flip (bool): Allow horizontal flip
    • holes (list): Interior holes as list of polygons
  • boundary: Sheet definition
    • width, height (float): Rectangle dimensions, OR
    • polygon (list): Custom boundary shape
  • config: Solver configuration (optional)
    • strategy (str): "blf", "nfp", "ga", "brkga", "sa", "gdrr", "alns"
    • spacing (float): Gap between geometries
    • margin (float): Gap from boundary
    • time_limit_ms (int): Timeout in milliseconds
    • population_size (int): GA/BRKGA population, at least 2, run as given. Omitted, each search uses the size it is tuned for: 30 in 2D
    • max_generations (int): GA/BRKGA generation budget, at least 0, run as given. Omitted, 50 in 2D
    • seed (int): RNG seed of ga, brkga, sa, gdrr and alns. The same problem with the same seed returns the same layout when the run ends on its own budget rather than on the clock (a 2D search keeps a time budget of its own, 10-15 s, when time_limit_ms is 0)
    • multi_sheet (bool): Distribute overflow across multiple sheets (default: False). When True, parts that do not fit on one sheet spill onto extra sheets instead of becoming unplaced; boundaries_used reports the sheet count and each placement's boundary_index selects its sheet with sheet-local coordinates.

Returns: Dictionary with:

  • success (bool): Whether solve succeeded
  • placements (list): Placement results (instance-level)
  • utilization (float): Area utilization ratio
  • boundaries_used (int): Number of sheets used (>1 only when multi_sheet=True)
  • total_requested (int): Σ of every geometry's quantity (instance-level total). Unplaced instance count = total_requested - len(placements)
  • unplaced (list): Deduplicated IDs of items that couldn't be placed (not per-instance, so len(unplaced) under-reports the failed-instance count)
  • computation_time_ms (int): Solve time
  • error (str | None): Readable text when the solver refused the problem
  • code (str | None): Stable reason for that refusal (invalid_geometry, duplicate_id, parameter_out_of_range, ...)
  • details (dict | None): The values behind code (id, first, index, parameter, got, ...). A range gives each bounded side as min / max when the bound itself is accepted, or exclusive_min / exclusive_max when it is not

solve_3d(geometries, boundary, config=None) -> dict

Solve a 3D bin packing problem.

Parameters:

  • geometries: List of box definitions
    • id (str): Unique identifier
    • dimensions (list): [width, depth, height]
    • quantity (int): Number of copies
    • mass (float): Weight, at least 0 (optional)
  • boundary: Container definition
    • dimensions (list): [width, depth, height], each above 0
    • max_mass (float): Weight limit, above 0 (optional)
    • gravity (bool): Enable gravity constraint
    • stability (bool): Enable stability constraint
  • config: Same as solve_2d but multi_sheet, which raises ValueError (one container, no sheets), and:
    • strategy: "blf", "ep", "ga", "brkga", "sa"
    • population_size / max_generations omitted: 100 / 500 for ga, 50 / 100 for brkga

Returns: Same structure as solve_2d

Strategy Selection Guide

Strategy Speed Quality Best For
blf Fast Good Large instances, quick results
nfp Medium Better 2D with complex shapes
ep Fast Good 3D bin packing
ga Slow Best Small instances, max quality
brkga Slow Best Complex constraints
sa Medium Better Balanced speed/quality
gdrr Medium Better 2D, ruin-and-recreate on dense layouts
alns Medium Better 2D, adaptive neighborhood search

Requirements

  • Python 3.8+
  • No additional dependencies

License

MIT License - see LICENSE for details.

Metadata

Release files for u-nesting 0.18.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for u-nesting 0.18.0
File
u_nesting-0.18.0-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
u_nesting-0.18.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.8 abi3 Linux glibc 2.17+ x86-64 Details
u_nesting-0.18.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.8 abi3 Linux glibc 2.17+ ARM64 Details
u_nesting-0.18.0-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details
u_nesting-0.18.0-cp38-abi3-macosx_10_12_x86_64.whl CPython 3.8 abi3 macOS 10.12+ x86-64 Details

Total release size: 4.0 MB

Release files / u_nesting-0.18.0-cp38-abi3-win_amd64.whl

Download URL u_nesting-0.18.0-cp38-abi3-win_amd64.whl
Size 800.3 kB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
b33a555829b14e644a94688359b8369ce38fa0ba6c356f318f71b721abaa3b0a
BLAKE2b-256 checksum
How to use checksums
e3227397979b868a500522697f552f3e14c5577ab4c25bbeabd50c33a8de2444
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / u_nesting-0.18.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL u_nesting-0.18.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 853.0 kB
Tags CPython 3.8 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
2247b1a8b33a97ecda83085fa747539884d083c617747bcb8c20e6d9a5852472
BLAKE2b-256 checksum
How to use checksums
e68201adfd113d5fa1245c4d6193ca59223d1b15d7d962d72e7c6d98b92fd23f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / u_nesting-0.18.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL u_nesting-0.18.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 787.6 kB
Tags CPython 3.8 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
14eb24469db3ee89e2ac7609c17230717c7e9d5e8fb0a5d8786a7c4e7518373f
BLAKE2b-256 checksum
How to use checksums
a3571449ff6be75ca08d05003bd34811b5caba66ff777143f647b5bda62944ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / u_nesting-0.18.0-cp38-abi3-macosx_11_0_arm64.whl

Download URL u_nesting-0.18.0-cp38-abi3-macosx_11_0_arm64.whl
Size 763.9 kB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
82da330fd99097917cd825d2015afac872a79bcd0e76881aa9e75e4b4299857d
BLAKE2b-256 checksum
How to use checksums
6a8923a0680e8f47753c7576ae5b76507e17c0669b2fa8886e6678ab8a9b07ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / u_nesting-0.18.0-cp38-abi3-macosx_10_12_x86_64.whl

Download URL u_nesting-0.18.0-cp38-abi3-macosx_10_12_x86_64.whl
Size 828.8 kB
Tags CPython 3.8 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
4972f0e596b79a2ab9964d12e2a5a3d37e2466123cdd2ece7f71893ce4d2bea5
BLAKE2b-256 checksum
How to use checksums
ac23aa70a5ff019c3bce675889d8ca5c3186e6c79a9399137dbcc83b686aa63c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.18.0 This release

5 release files

0.14.0

5 release files

0.13.0

5 release files

0.12.0

5 release files

0.11.0

5 release files

0.9.0

5 release files

0.7.2

5 release files

0.7.1

5 release files

0.7.0

5 release files

0.6.0

5 release files

0.5.2

5 release files

0.5.1

5 release files

0.5.0

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page