Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mpee-0.1.0.tar.gz (5.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mpee-0.1.0-cp38-abi3-macosx_11_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

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

Hashes for mpee-0.1.0.tar.gz
Algorithm Hash digest
SHA256 013e4f88b0ef04c4c76574716ca6114a89c080231bc4fae0d62d552637e92409
MD5 415e86b6de8644ba6193034657e30de3
BLAKE2b-256 a2eea5cd47acd52aa30f3a55cdd79506a037802bdb4c977f3df6d2aabf3b56c4

See more details on using hashes here.

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

Hashes for mpee-0.1.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c2db1ee6766fe905fd7b426bfae5c4dcb9e4b7d7bb18010bc3d7dca279ef80d6
MD5 bffba8f6cbe6b6df7f2071ed771e1522
BLAKE2b-256 c1703f4806cc258ad81a58dc799ac66407bc155eb57872864c75972198587d4d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page