Conversation
added 14 commits
July 15, 2026 15:53
…on + use inherit function instead of list length
Many-to-many combination previously rescanned every node for every origin/destination pair (O(nb) per cell). Build per-node buckets from each destination's downward search and only combine against the nodes an origin's search actually touches instead.
…tance_matrix for all algorithms | normal,CH,CCH
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Integrates Customizable Contraction Hierarchies (CCH) support from the
cppRoutingCCHfork intocppRouting, fixing correctness and performance bugs found along the way, and bringing CCH to full functional parity with classical CH (contracted graphs) wherever that made sense.Guiding principle: every R function that supports a CH graph should also support a CCH graph, and vice versa where the underlying algorithm allows it.
CCH correctness and performance fixes
The fork's CCH backend had several bugs and unoptimized code paths that predate this integration:
get_path_pair()returned wrong nodes on a CCH graph. Root cause:CCHPathPairWorkerdidn't reset its touched-node state between pairs in a batch, and self-pairs (from == to) weren't handled. Both fixed;CCHDistancePairWorker/CCHPathPairWorkerwere also rewritten to use the elimination-tree query algorithm (ascend viafor_ancestors/relax_outgoing_elimination, descend pinned ancestors viarelax_incoming_elimination) instead of a bidirectional priority-queue search, matching the already-correctCCHPathValuesPairWorkerpattern.get_path_pair()on a CCH graph is new — the fork never supported this.min_degree_order()(the elimination ordering heuristic) was O(n²) — a linear scan for the minimum-degree node at every one of the n elimination steps. Replaced with a bucket-by-degree queue (buckets indexed by current degree, a monotonic-with-backward-reset pointer to the smallest non-empty bucket), bringing this to near-linear time while preserving the exact same tie-breaking (smallest node id among degree ties), verified against the original algorithm on real data.distance_matrix_cch()rescanned all buckets for every destination on every cell (O(n·b)). Rewritten RPHAST-style: downward searches build per-node buckets of (destination, distance) once, then upward searches combine buckets directly.Stall_par), which is standard for classical CH, does not apply to CCH's dense clique-closure topology (confirmed via instrumentation: 0% trigger rate) — left out of the CCH query path rather than adding dead-weight checks.cpp_simplify()fixescpp_simplify()was an unclassed list. This silently broke every downstream function that checksinherits(Graph, "cppRouting_graph")(get_distance_pair,get_distance_matrix,aggregate_aux, etc. on a simplified graph), producing confusing "object not found"-style errors instead of a clear validation message. Fixed by classing the resultc("cppRouting_simplified", "cppRouting_graph")— multi-class inheritance keeps every existinginherits(x, "cppRouting_graph")check working while allowing new simplified-graph-specific dispatch.edge_origin, mirrored through every merge insimp()/simplify()), and summing the requested aux columns once at the end.aggregate_aux: unified multi-column auxiliary weightsThis is the main feature of this integration.
makegraph()'sauxargument now accepts adata.framewith one or more named columns (in addition to the historical single vector), andaggregate_auxonget_distance_pair()/get_distance_matrix()acceptsTRUE(all columns) or a character vector of column names — consistently across normal graphs, CH, CCH, and simplified graphs.The fork's equivalent (
get_path_values_pair(), CCH-only, values supplied at query time rather than declared on the graph) is superseded by this: auxiliary weights are now a graph-level property declared once atmakegraph()time, reused across every query function, not a special-purpose CCH-only query.Aggregation happens inline in C++, during the same search that computes distances — no path reconstruction, no R round-trip:
Dijkstra,bi,A*,NBA):distancePairAdd/distanceMatAdd, generalizing the existing single-columnadd/addrrunning-sum mechanism (distancePair/distanceMat) from one running sum to N, using a flattened column-majornbedge × n_auxlayout.distancePairCAdd/phastCAdd, fed by a per-shortcut N-column precompute pass (aggCAdd, generalizingaggC) so the query itself never unpacks shortcuts.cpppathvaluescchengine.Requesting more than one
auxcolumn changes the return shape, and this is new:get_distance_pair()returns adata.frame(one column per requestedauxname) instead of a plain vector, andget_distance_matrix()returns a namedlistof matrices (one per requestedauxname) instead of a single matrix. The historical single-column shape (plain vector / single matrix) is preserved whenever exactly oneauxvalue is requested, whetherauxwas declared as a single vector (aggregate_aux = TRUE) or as a one-column selection out of adata.frameaux(aggregate_aux = TRUEwith one column, oraggregate_aux = "name") - both distinguished internally bysingle_legacyinresolve_aggregate_aux()/shape_aggregate_result()(aux_helpers.R).API cleanup
cpp_cch_prepare()merged intocpp_contract(): newcustomizable(defaultFALSE) andorderarguments.customizable = FALSEis the unchanged classical CH path;customizable = TRUEruns what used to becpp_cch_prepare()and returns a CCH topology object. One entry point for both contraction flavors.cpp_cch_customize()renamed tocpp_customize().cpp_cch_prepare()removed. All call sites updated (assign_traffic()'s auto-preparation, error messages acrossget_aon/get_distance_pair/get_distance_matrix),NAMESPACEand generatedman/pages regenerated withroxygen2.CCH preparation progress bar
cpp_contract(..., customizable = TRUE)previously had no progress feedback at all (unlike classicalcpp_contract(), which shows one viaRcppProgress). Added the same mechanism to both phases of CCH preparation — elimination-order computation (min_degree_order) and the node-elimination/shortcut-construction loop (build_cch) — controlled by the existingsilentargument, which is no longer ignored whencustomizable = TRUE. Also addedRcpp::checkUserInterrupt()to both loops, matching the classical CH path.Fixes found during a final integration review
A systematic diff against
cppRoutingCCH(every exported R function, every C++ file) turned up a couple of things that had been ported partially:validate_cch_for_graph()— which checks that a user-suppliedcchobject was actually prepared from the same graph topology beforeassign_traffic()uses it — existed incch.Rbut its call site inassign_traffic()had never been ported. Acchprepared for the wrong graph previously hit undefined behavior in the C++ layer instead of a clear R error. Wired back in and tested against both a matching and a mismatched topology.assign_traffic()'s roxygen docs didn't mentionaon_method = "cch"at all — missing from the@param aon_methodenum, no@param cchentry, and the AON algorithm list in@detailsomitted it entirely, even though the code has fully supported it. Docs restored (adapted for thecpp_contract/cpp_customizerenames) andman/assign_traffic.Rdregenerated.aggc.cppprinted a diagnostic then indexed with-1(undefined behavior) when a shortcut's original edge couldn't be found, in both the pre-existingaggCand the newaggCAdd— now raises a clearRcpp::stop(). A stale roxygen claim thataggregate_aux's character-vector column-selection form was "CCH only" was removed (it's been generally supported for a while).man/*.Rdpages had drifted out of sync with their roxygen source comments across several prior sessions (roxygen2was not installed in this environment). Installed and regenerated;NAMESPACE(hand-maintained, not roxygen-generated) was left untouched.Aside from those, the review confirmed cppRouting is at full parity with (and in some places ahead of — CCH support in
get_path_pair, the multi-columnaggregate_aux, and the performance fixes above are not in the fork)cppRoutingCCH. The only remaining gaps areget_multi_paths()/get_isochrone()/get_detour(), which support neither CH nor CCH in either codebase — a pre-existing, deliberately out-of-scope limitation, not something this integration missed.