Skip to content

Commit e23830d

Browse files
committed
add order picking / jpype blog
2 parents 42de2c1 + a4552b2 commit e23830d

File tree

11 files changed

+2533
-78
lines changed

11 files changed

+2533
-78
lines changed

content/en/blog/technical/order-picking-quickstart-jpype-performance.md

Lines changed: 37 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
---
2-
title: "Order Picking Quickstart: When Good Architecture Reveals Infrastructure Limits"
2+
title: "Order Picking Quickstart: JPype Bridge Overhead in Constraint Solving"
33
date: 2025-12-24
4-
tags: [quickstarts, python, performance]
4+
draft: false
5+
tags: [quickstart, python]
56
description: >
6-
Introducing the Order Picking quickstart with real-time 3D visualization—and an honest look at JPype performance bottlenecks.
7+
Building an order picking quickstart with real-time 3D visualization deepened our understanding of JPype's performance characteristics in constraint-heavy workloads.
78
---
89

9-
We just shipped the final quickstart in our first iteration: [Order Picking](https://github.com/SolverForge/solverforge-quickstarts/tree/main/fast/order-picking-fast). It solves warehouse optimization with real-time isometric visualization showing trolleys dynamically routing through shelves to pick orders.
10+
Our current constraint solving quickstarts in Python are based on our stable, legacy fork of [Timefold](https://www.timefold.ai) for Python, which uses JPype to bridge to Timefold's Java solver engine. The latest example is [Order Picking](https://github.com/SolverForge/solverforge-quickstarts/tree/main/fast/order-picking-fast)—a warehouse optimization problem with real-time isometric visualization showing trolleys routing through shelves to pick orders.
1011

11-
It works well. It's also slower than we'd like. That tension—between what works architecturally and what performs optimally—is worth examining.
12+
The implementation works and demonstrates the architectural patterns we've developed. It also exposes the inherent overhead of FFI (Foreign Function Interface) bridges in constraint-heavy workloads.
1213

13-
## The Problem
14+
## The Problem Domain
1415

1516
Order picking is the warehouse operation where workers (or trolleys) collect items from shelves to fulfill customer orders. The optimization challenge combines:
1617

1718
- **Capacity constraints**: trolleys have buckets with volume limits, products have different sizes
1819
- **Routing constraints**: minimize travel distance, efficient sequencing
1920
- **Assignment constraints**: each item picked exactly once, balance load across trolleys
2021

21-
This maps to vehicle routing with bin packing characteristics.
22+
This maps to vehicle routing with bin packing characteristics—a constraint-intensive problem domain.
2223

2324
## Real-Time Visualization
2425

25-
The UI renders an isometric warehouse with five trolleys navigating between shelving units. Routes update live as the solver reassigns items, color-coded to show which trolley picks which items. The visualization polls solver state every 250ms and renders at 60fps using HTML5 Canvas.
26+
The UI renders an isometric warehouse with trolleys navigating between shelving units. Routes update live as the solver reassigns items, color-coded to show which trolley picks which items.
2627

27-
Getting real-time updates working required solving a JPype-specific challenge. The solver runs in a Java thread and modifies domain objects that Python needs to read. We cache solutions in callbacks (`with_first_initialized_solution_consumer`, `with_best_solution_consumer`) so the API can serve them without crossing the Python-Java boundary on every poll:
28+
Not only solving itself, but merely getting real-time updates working required tackling JPype-specific challenges. The solver runs in a Java thread and modifies domain objects that Python needs to read. To avoid crossing the Python-Java boundary on every poll, solutions are cached in solver callbacks:
2829

2930
```python
3031
@app.get("/schedules/{problem_id}")
@@ -42,43 +43,24 @@ async def get_solution(problem_id: str) -> Dict[str, Any]:
4243
return result
4344
```
4445

45-
The frontend detects when paths change and smoothly transitions between routes:
46+
This pattern—caching solver state in callbacks, serving from cache—avoids *some* JPype overhead in the hot path of UI polling.
4647

47-
```javascript
48-
function updateWarehouseAnimation(solution) {
49-
if (!userRequestedSolving) return;
48+
## Performance Characteristics
5049

51-
for (const trolley of solution.trolleys || []) {
52-
const stepIds = (trolley.steps || []).map(ref =>
53-
typeof ref === 'string' ? ref : ref.id
54-
);
55-
const newSignature = stepIds.join(',');
50+
In spite of the above hack, the JPype bridge still introduces major overhead that becomes very significant in constraint-heavy problems like order picking. The overhead is expacted to grow exponentially with scale.
5651

57-
if (existingAnim && oldSignature !== newSignature) {
58-
existingAnim.path = buildTrolleyPath(trolley, steps).path;
59-
existingAnim.stepSignature = newSignature;
60-
existingAnim.startTime = Date.now();
61-
}
62-
}
52+
The solver's work happens primarily in:
53+
- **Constraint evaluation**: Checking capacity limits, routing constraints, assignment rules
54+
- **Move generation**: Creating candidate solutions (reassigning items, reordering routes)
55+
- **Score calculation**: Computing solution quality after each move
56+
- **Shadow variable updates**: Cascading capacity calculations through trolley routes
6357

64-
renderWarehouse(solution);
65-
}
66-
```
67-
68-
## Performance Numbers
69-
70-
Here's where we need to be honest: this is noticeably slower than Java.
71-
72-
Running on the default problem (5 trolleys, 8 orders, ~40 steps):
73-
74-
| Implementation | 30-second solve iterations | Score achieved |
75-
|----------------|---------------------------|----------------|
76-
| Java (Timefold) | ~500-600 | 0hard/-8000soft |
77-
| Python (dataclass) | ~200-250 | 0hard/-12000soft |
58+
For order picking specifically, the overhead compounds from:
59+
- **List variable manipulation** (`PlanningListVariable`): Frequent reordering of trolley pick lists
60+
- **Shadow variable cascading**: Capacity changes ripple through entire routes
61+
- **Equality checks**: Object comparison during move validation
7862

79-
Python completes roughly 40% of the iterations Java manages in the same timeframe, and reaches a less optimal score.
80-
81-
The solver spends time in constraint evaluation, move generation, and score calculation. For order picking specifically, there's overhead from list variable manipulation (`PlanningListVariable`), shadow variable updates (cascading capacity calculations), and equality checks during move validation.
63+
Each of these operations crosses the Python-Java boundary through JPype, and these crossings happen millions of times during solving.
8264

8365
## Why JPype Specifically
8466

@@ -93,15 +75,18 @@ def define_constraints(factory: ConstraintFactory):
9375
]
9476
```
9577

96-
Every constraint evaluation triggers JPype conversions. Even with dataclass optimization (avoiding Pydantic overhead in hot paths), we can't eliminate the FFI cost.
78+
Every constraint evaluation triggers JPype conversions. Even with [dataclass optimization]((/blog/technical/python-constraint-solver-architecture/))(avoiding Pydantic overhead in hot paths), we can't eliminate the FFI cost.
9779

98-
Some operations are more affected:
80+
The operations most affected by bridge overhead:
9981

100-
- **List operations**: `PlanningListVariable` for trolley steps requires frequent list manipulation, each crossing to Java
101-
- **Shadow variables**: capacity calculations cascade through step lists, triggering Java calls
102-
- **Equality checks**: object comparison during move validation crosses the boundary
82+
- **List operations**: `PlanningListVariable` for trolley steps requires frequent list manipulation
83+
- **Shadow variables**: capacity calculations cascade through step lists
84+
- **Equality checks**: object comparison during move validation
10385

104-
What actually helps: callback-based caching (storing serialized solutions), thread pool for analysis (running `solution_manager.analyze()` in ThreadPoolExecutor), and minimizing domain model complexity (fewer fields, fewer conversions).
86+
Mitigation strategies that help:
87+
- **Callback-based caching**: Store serialized solutions to avoid repeated boundary crossings
88+
- **Simplified domain models**: Fewer fields means fewer conversions
89+
- **Dataclass over Pydantic**: Skip validation overhead in solver hot paths (see [architecture comparison](/blog/technical/python-constraint-solver-architecture/))
10590

10691
## Why This Validates Rust
10792

@@ -117,9 +102,9 @@ We're building a constraint solver framework in Rust with WASM + HTTP architectu
117102

118103
With Rust/WASM, the order picking implementation would eliminate all JPype overhead and run constraint evaluation at native speed while keeping the same domain model structure. The architecture stays the same. The performance gap disappears.
119104

120-
## Try It
105+
## Source Code
121106

122-
**Live demo:** [Hugging Face Spaces](https://huggingface.co/spaces/solverforge/order-picking)
107+
**Repository:** [SolverForge Quickstarts](https://github.com/SolverForge/solverforge-quickstarts/tree/main/fast/order-picking-fast)
123108

124109
**Run locally:**
125110
```bash
@@ -131,24 +116,11 @@ pip install -e .
131116
run-app
132117
```
133118

134-
**Source:** All quickstarts follow the architectural pattern documented in [dataclasses vs Pydantic](/blog/technical/python-constraint-solver-architecture/).
135-
136-
## What's Next
137-
138-
Completed first iteration:
139-
- Employee Scheduling
140-
- Meeting Scheduling
141-
- Vehicle Routing
142-
- Order Picking
143-
144-
Next iteration adds maintenance scheduling, school timetabling, task assignment, and resource allocation.
119+
**Architecture:** All quickstarts follow the pattern documented in [dataclasses vs Pydantic](/blog/technical/python-constraint-solver-architecture/).
145120

146-
The Rust framework is in active development:
147-
- Q1 2025: Alpha release with basic constraint types
148-
- Q2 2025: Feature parity with Python quickstarts
149-
- Q3 2025: Production-ready 1.0 with WASM compilation
121+
**Rust framework development:**
150122

151-
Follow progress at [github.com/SolverForge](https://github.com/SolverForge)
123+
The Rust/WASM framework is in early development. Follow progress at [github.com/SolverForge](https://github.com/SolverForge).
152124

153125
---
154126

content/en/docs/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ menu: {main: {weight: 20}}
77
Welcome to the SolverForge documentation. Choose a section to get started:
88

99
- **[Overview](overview.md)** - Project Overview and roadmap
10+
- **[Concepts](concepts/)** - Introduction to PlanningAI
1011
- **[Getting Started](getting-started/)** — Quickstart guides and tutorials (legacy Python examples)
1112
- **[Rust Solver](solverforge/)** — Rust-based constraint solver with WASM modules and HTTP communication
1213
- **[Python Solver](solverforge-legacy/)** — Technical documentation for the Python constraint solver

content/en/docs/solverforge-legacy/concepts/_index.md renamed to content/en/docs/concepts/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Concepts"
33
linkTitle: "Concepts"
4-
weight: 10
4+
weight: 2
55
tags: [concepts, python]
66
description: >
77
Understand the fundamental concepts of planning optimization and constraint solving.
File renamed without changes.
File renamed without changes.

content/en/docs/solverforge-legacy/concepts/what-is-planning.md renamed to content/en/docs/concepts/what-is-planning.md

File renamed without changes.

content/en/docs/getting-started/_index.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ Schedule lessons to rooms and timeslots without teacher or room conflicts.
4646

4747
[Start Tutorial →](school-timetabling/)
4848
{{< /card >}}
49+
{{< card header="**Portfolio Optimization**" >}}
50+
Select stocks for a diversified portfolio while maximizing expected returns.
51+
52+
[Start Tutorial →](portfolio-optimization/)
53+
{{< /card >}}
54+
{{< card header="**VM Placement**" >}}
55+
Place virtual machines on servers respecting capacity, affinity, and consolidation goals.
56+
57+
[Start Tutorial →](vm-placement/)
58+
{{< /card >}}
59+
{{< /cardpane >}}
60+
61+
{{< cardpane >}}
4962
{{< card header="**Rust Quickstart**" footer="Experimental" >}}
5063
Build a solver using the core Rust library directly. For advanced users interested in the internals.
5164

@@ -73,6 +86,8 @@ Common fast quickstarts available now:
7386
- `fast/meeting-scheduling-fast`
7487
- `fast/vehicle-routing-fast`
7588
- `fast/employee-scheduling-fast`
89+
- `fast/portfolio-optimization-fast`
90+
- `fast/vm-placement-fast`
7691

7792
Each use case folder includes a README describing how to run the example, expected inputs, and any implementation-specific details.
7893

content/en/docs/getting-started/meeting-scheduling.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
title: "Meeting Scheduling"
33
linkTitle: "Meeting Scheduling"
44
icon: fa-brands fa-python
5-
date: 2025-11-26
5+
date: 2025-12-22
66
weight: 20
7-
draft: true
7+
draft: false
88
description: "A comprehensive quickstart guide to understanding and building intelligent meeting scheduling with SolverForge"
99
categories: [Quickstarts]
1010
tags: [quickstart, python]
@@ -2375,7 +2375,7 @@ Each quickstart teaches complementary optimization techniques.
23752375

23762376
### Additional Resources
23772377

2378-
- [SolverForge Documentation](https://docs.solverforge.ai)
2378+
- [SolverForge Documentation](https://www.solverforge.org/docs)
23792379
- [Meeting Scheduling Problem Overview](https://en.wikipedia.org/wiki/Meeting_scheduling_problem)
23802380
- [GitHub Repository](https://github.com/solverforge/solverforge-quickstarts)
23812381
- [Calendar Scheduling Algorithms](https://www.cs.cmu.edu/~awm/papers/scheduling.pdf)

0 commit comments

Comments
 (0)