MPEE (Morten Punnerud-Engelstad Engine) — Python bindings for the in-process Rust routing (dijeng) + VRP solver (brooom). Load the engine from Python; the solver and the interpreter share one address space.
Project description
mpee-py
Part of the mpee workspace.
PyO3 bindings that expose mpee's in-process VRP pipeline
(brooom + sssp_bench) as a Python extension module. Drop-in for a
Flask / FastAPI / Streamlit front-end without dropping the
shared-memory architecture: Python and Rust live in the same process,
so the JSON bytes the browser fetches come straight out of the same
Arc<String> that the Rust solver thread just populated.
The headline use-case is the macOS Application Firewall: every
fresh mpee-serve binary requires per-build approval before non-
loopback connections work. Python (Apple-signed system binary) is
already allowed by default — so python3 app.py binds 0.0.0.0:8032
without prompts.
Architecture
┌──── Python process (Flask + mpee) ────────────────┐
│ │
│ import mpee │
│ eng = mpee.Engine() │
│ eng.start_solve(region="london", n_jobs=500, ...) │
│ │ │
│ ▼ std::thread::spawn (inside Rust) │
│ ┌────────────────────────────────────────────────┐ │
│ │ Rust solver thread: │ │
│ │ sssp_bench::load_mmap → matrix_with_distance │ │
│ │ brooom::solve_with_matrix (iterative) │ │
│ │ publishes Arc<String> after every chunk │ │
│ └────────────────────────────────────────────────┘ │
│ │ │
│ ▼ engine.get_dataset_json() (no copy) │
│ Flask routes: /api/status /api/dataset / │
│ │ │
└─────────────┼────────────────────────────────────────┘
▼
📱 phone on the same network
The boundary between Python and Rust is two thin methods:
get_status_json() and get_dataset_json(). Both read from the
shared Arc<RwLock<AppState>>. The JSON string is already
serialised in Rust — Python just returns its bytes verbatim.
Build & run
# From the repo root (or anywhere — venv lives in this crate dir):
cd crates/mpee-py
# Once: set up a Python venv and install build tools.
python3 -m venv venv
source venv/bin/activate
pip install maturin flask
# Builds the Rust extension and installs it into the venv.
maturin develop --release
# Starts the Flask server (defaults to London N=500, 0.0.0.0:8032).
python3 python/app.py
The first maturin develop --release takes about 30–60 s (full
optimised LTO build of brooom + sssp_bench + bindings). Subsequent
incremental builds are seconds.
On macOS this avoids the per-binary Application Firewall prompt
that mpee-serve triggers — Python is already trusted by the firewall.
Python API
import mpee
import json, time
eng = mpee.Engine()
eng.start_solve(
region="london", # "london" / "oslo" / "manhattan" / "paris"
n_jobs=500,
n_vehicles=20,
capacity=200,
seed=7,
ch="data/greater-london.osm.pbf.ch",
pp="data/greater-london.osm.pbf.pp",
time_limit_s=45.0,
multi_start=1,
)
while not eng.is_done():
status = json.loads(eng.get_status_json())
print(status["state"], status["phase"], status["message"])
ds = eng.get_dataset_json()
if ds is not None:
# Same JSON shape as mpee-serve's /api/dataset bundle.
bundle = json.loads(ds)
print(f"iter {bundle['iter']}: cost={bundle['cost']:.0f}")
time.sleep(1)
print("final dataset_iter:", eng.dataset_iter())
Method reference
| Method | Returns | Notes |
|---|---|---|
Engine() |
Engine |
Construct an idle engine. |
start_solve(...) |
None |
Spawn the background solver thread. |
get_status_json() |
str |
Status (state, phase, message, progress, elapsed_s, dataset_iter, config). |
get_dataset_json() |
Optional[str] |
Latest dataset, or None until first iter. |
dataset_iter() |
int |
Current published iteration counter. |
is_done() |
bool |
True once the solver finished (or failed). |
state() |
str |
One of idle / solving / evolving / done / failed. |
start_solve(...) arguments mirror the mpee-serve CLI:
start_solve(
region: str,
n_jobs: int,
n_vehicles: int,
capacity: int,
seed: int,
ch: str,
pp: str,
time_limit_s: float = 45.0,
multi_start: int = 1,
)
Endpoints exposed by python/app.py
Identical to mpee-serve so the embedded index.html works unchanged:
| Method | Path | Description |
|---|---|---|
| GET | / |
Embedded Leaflet HTML (mobile UI). |
| GET | /api/status |
Live status JSON (polled by the UI). |
| GET | /api/dataset |
200 + dataset, or 202 + status while pre-first-iter. |
| GET | /api/health |
{"ok":true} |
CORS is wide-open. The UI polls /api/status every ~2 s and re-renders
the map whenever dataset_iter advances.
Why not just disable the firewall?
Doing socketfilterfw --setglobalstate off works but turns off all
incoming protection. Adding --add ./target/release/mpee-serve works
per-binary but every cargo build changes the binary's code signature,
so the rule expires and the firewall blocks the next launch silently.
Going through Python sidesteps that entirely.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mpee-0.1.0.tar.gz.
File metadata
- Download URL: mpee-0.1.0.tar.gz
- Upload date:
- Size: 5.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
013e4f88b0ef04c4c76574716ca6114a89c080231bc4fae0d62d552637e92409
|
|
| MD5 |
415e86b6de8644ba6193034657e30de3
|
|
| BLAKE2b-256 |
a2eea5cd47acd52aa30f3a55cdd79506a037802bdb4c977f3df6d2aabf3b56c4
|
File details
Details for the file mpee-0.1.0-cp38-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: mpee-0.1.0-cp38-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.8 MB
- Tags: CPython 3.8+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2db1ee6766fe905fd7b426bfae5c4dcb9e4b7d7bb18010bc3d7dca279ef80d6
|
|
| MD5 |
bffba8f6cbe6b6df7f2071ed771e1522
|
|
| BLAKE2b-256 |
c1703f4806cc258ad81a58dc799ac66407bc155eb57872864c75972198587d4d
|