Crate routingkit_cch

Crate routingkit_cch 

Source
Expand description

§routingkit-cch

Crates.io Docs License

Rust bindings for the Customizable Contraction Hierarchies (CCH) implementation from RoutingKit. CCH is a three‑phase shortest path acceleration technique for large directed graphs (e.g. road networks) that allows fast re-weighting while keeping very low query latency.

§Why CCH?

CustomizableContractionHierarchies (CCH) are an index-based speedup technique for shortest paths in directed graphs that can quickly be adapted to new weights. CCHs use, contrary to regulars CHs, a three phase setup:

  1. Preprocessing
  2. Customization
  3. Query

The preprocessing is slow but does not rely on the arc weights. The Customization introduces the weights and is reasonably fast. Finally, the actual paths are computed in the query phase. A common setup consists of doing the preprocessing once and the customization per user upon login. Further one can use a customization to incorporate live-traffic updates.

§Features

  • Safe ergonomic Rust API on top of proven C++ core via cxx.
  • Build indices from raw edge lists (tail/head arrays).
  • Ordering helpers: nested dissection (inertial separator heuristic) and degree fallback.
  • Sequential & parallel customization, and partial weight update afterwards.
  • Reusable query object supporting multi-source / multi-target searches.
  • Path extraction: node sequence & original arc id sequence.
  • Thread-safe sharing of immutable structures (CCH, CCHMetric).

§Installation

Stable release from crates.io:

[dependencies]
routingkit-cch = "0.1"

Or track the repository:

[dependencies]
routingkit-cch = { git = "https://github.com/HellOwhatAs/routingkit-cch" }

For the git form ensure the RoutingKit submodule is present:

git submodule update --init --recursive

Requirements: C++17 compiler (MSVC / gcc / clang).
OpenMP is enabled automatically by the build script; ensure your toolchain provides an OpenMP runtime (e.g. install libomp on macOS). Without it the build may fail when CCHMetric::parallel_new is called.

§Quick Start

use routingkit_cch::{CCH, CCHMetric, CCHQuery, compute_order_degree};

// Small toy graph: 0 -> 1 -> 2 -> 3
let tail = vec![0, 1, 2];
let head = vec![1, 2, 3];
let weights = vec![10, 5, 7]; // total 22 from 0 to 3
let node_count = 4u32;

// 1) Compute a (cheap) order; for real data prefer compute_order_inertial (requires lat,lon).
let order = compute_order_degree(node_count, &tail, &head);
let cch = CCH::new(&order, &tail, &head, false);

// 2) Bind weights & customize (done inside CCHMetric::new here).
let metric = CCHMetric::new(&cch, weights.clone());

// 3) Run a shortest path query 0 -> 3.
let mut q = CCHQuery::new(&metric);
q.add_source(0, 0);
q.add_target(3, 0);
q.run();
assert_eq!(q.distance(), Some(22));
let node_path = q.node_path();
assert_eq!(node_path, vec![0, 1, 2, 3]);
let arc_path = q.arc_path();
assert_eq!(arc_path, vec![0, 1, 2]);

§Building an Order

For production use prefer the inertial nested dissection based order:

use routingkit_cch::compute_order_inertial;
let order = compute_order_inertial(node_count, &tail, &head, &latitude, &longitude);

Better separators -> faster customization & queries. External advanced orderers (e.g. FlowCutter) could be integrated offline; you only need to supply the permutation.

§(Parallel) Customization

use routingkit_cch::{CCH, CCHMetric};
let metric = CCHMetric::new(&cch, weights.clone()); // single thread
let metric = CCHMetric::parallel_new(&cch, weights.clone(), 0); // 0 -> auto threads

Use when graphs are large enough; for tiny graphs overhead may outweigh benefit.

§Incremental (Partial) Weight Updates

If only a small subset of arc weights change (e.g. traffic incidents), you can avoid a full re-customization:

let mut metric = CCHMetric::parallel_new(&cch, weights.clone(), 0);
let mut updater = CCHMetricPartialUpdater::new(&cch);
// ... run queries ...
// Update two arcs (id 12 -> 900, id 77 -> 450)
updater.apply(&mut metric, &BTreeMap::from_iter([(12, 900), (77, 450)]));
// New queries now see updated weights.

§Query

let mut q = CCHQuery::new(&metric);
q.add_source(0, 0);
q.add_target(3, 0);
q.run();
printf("{}", q.distance());
q.reset(); // much cheaper than `CCHQuery::new`
q.add_source(2, 0);
q.add_target(5, 0);
q.run();
// ...

CCHQuery is not thread-safe; create one instance per thread and reuse it within thread via reset(), which is far cheaper than CCHQuery::new.

§Path Reconstruction

After run():

  • distance() -> Option<u32> (None = unreachable)
  • node_path() -> Vec<node_id> (empty = unreachable)
  • arc_path() -> Vec<original_arc_id> (empty = unreachable)

§Thread Safety

TypeSendSyncNotes
CCHyesyesImmutable after build
CCHMetricyesyesRead-only after customization / partial-update
CCHQueryyesnoInternal mutable labels; reuse with reset()
CCHMetricPartialUpdaternonoShould have nothing to do with parallel

Create separate queries per thread for parallel batch querying.

Modules§

ffi
shp_utils

Structs§

CCH
CCHMetric
CCHMetricPartialUpdater
Reusable partial customization helper. Construct once if you perform many small incremental weight updates; this avoids reallocating O(m) internal buffers each call.
CCHQuery

Functions§

compute_order_degree
compute_order_inertial