Skip to content

feat(algo): descriptor-template GDS layer — GDS_LEIDEN, GDS_LOUVAIN, GDS_PPR - #56

Merged
adsharma merged 3 commits into
LadybugDB:mainfrom
zachwinter:feat/gds-louvain-helper
Aug 13, 2026
Merged

feat(algo): descriptor-template GDS layer — GDS_LEIDEN, GDS_LOUVAIN, GDS_PPR#56
adsharma merged 3 commits into
LadybugDB:mainfrom
zachwinter:feat/gds-louvain-helper

Conversation

@zachwinter

@zachwinter zachwinter commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Reworked per review — both points:

1. Boilerplate → descriptor templates. gds_algo_template.h introduces GDSPerNodeScalarAlgo<DESC>: one class template per algorithm shape (per-node-scalar is the largest equivalence class — centrality, community detection). The template owns bind, the shared zero-copy CSR bridge, GraphR construction, and result streaming; an algorithm is now a descriptor — name, output column, optional params, an extra-positional-args policy (NoExtraArgs / SourceListArg), and the NetworKit invocation. The 100+ algorithms become ~30–100 line descriptor files, not PRs full of copied plumbing. GDS_PAGE_RANK is refactored onto it in this PR (CALL surface and test expectations unchanged). Per-node-vector outputs (GDS_NODE2VEC) stay bespoke until a second member of that class motivates the template.

2. Leiden first. GDS_LEIDEN via ParallelLeidenView (gamma + iterations optional params) — ~100 lines including both param structs, the first new algorithm to ride the template. GDS_LOUVAIN stays (a ~55-line descriptor now) since the two are useful to compare, but the Leiden file carries the "prefer this" note.

Bonus: GDS_PPRCALL GDS_PPR('G', [rowid, ...]) via PageRank::forSources, the SourceListArg policy's first user. Its test is worth a look: two 4-cliques joined by a bridge, anchored in one clique — trust mass stays with the anchor's community (0.17–0.29 vs 0.03–0.07 across the bridge). That's the confidence primitive from the Discord trust/viewpoint discussion, as a query.

Suite: 78/78 locally (all existing GDS test expectations unchanged by the refactor).

🤖 Generated with Claude Code

Louvain community detection via the icebug bridge: projected graph ->
InMemGraph CSR -> Arrow UInt64 arrays (system pool, see previous commit) ->
zero-copy NetworKit::GraphR -> PLM -> community assignments streamed back
through the GDS result pipeline.

Coexists with the hand-rolled LOUVAIN under the GDS_ prefix, same as
GDS_PAGE_RANK. Test asserts community structure (two 4-cliques joined by a
bridge edge), not raw IDs, since PLM is parallel and nondeterministic.

Co-Authored-By: Claude Fable 5 <[email protected]>
@zachwinter

Copy link
Copy Markdown
Contributor Author

Rebased over #47's merge (the gated source list and registration both conflicted — resolved as the union). 75/75 locally with node2vec and Louvain together.

@zachwinter
zachwinter force-pushed the feat/gds-louvain-helper branch from c76a58f to 3f4d6cf Compare August 13, 2026 18:34
@adsharma

Copy link
Copy Markdown
Contributor

Much of gds_xxx.cpp seems to be boilerplate. Is it possible to use some templates/reflection/codgen mechanism to automate it? Or we're looking at 100+ PRs.

Also ParallelLeidenView is a higher performance, better quality version of this algorithm, so I'd do that first.

zachwinter and others added 2 commits August 13, 2026 15:27
Random walk with restart from caller-supplied source nodes:
CALL GDS_PPR('G', [rowid, ...]) — teleportation is uniform over the
sources (NetworKit::PageRank::forSources, memory-efficient: no n-sized
personalization vector), so scores measure standing relative to the
anchors rather than globally. Consumes the shared zero-copy CSR bridge.

Optional params dampingFactor/tolerance as GDS_PAGE_RANK. Sources
validated non-empty and non-negative at bind, range-checked against the
projected node count at run time.

Tests: star anchored at a leaf (asymmetric distribution vs the plain
PageRank tie), and a two-clique trust-closure case — anchored in one
clique, mass stays with the anchor's community (0.17-0.29 vs 0.03-0.07
across the bridge) — plus empty-list and out-of-range error cases.

Co-Authored-By: Claude Fable 5 <[email protected]>
Per review: the per-algorithm boilerplate is gone. GDSPerNodeScalarAlgo
(gds_algo_template.h) supplies bind, the shared zero-copy CSR bridge,
GraphR construction, and vertex-compute streaming for the whole
per-node-scalar equivalence class; an algorithm is now a descriptor —
name, output column, optional params, extra-positional-arg policy
(NoExtraArgs / SourceListArg), and the NetworKit invocation.

GDS_PAGE_RANK, GDS_LOUVAIN, and GDS_PPR are rewritten as descriptors
(unchanged CALL surfaces, unchanged test expectations). GDS_LEIDEN is
the first new algorithm to ride the template: ParallelLeidenView with
gamma/iterations optional params, ~100 lines including both param
structs. Per-node-vector outputs (GDS_NODE2VEC) stay bespoke until a
second member of that class motivates the template.

Co-Authored-By: Claude Fable 5 <[email protected]>
@zachwinter zachwinter changed the title feat(algo): add GDS_LOUVAIN backed by icebug (NetworKit PLM) feat(algo): descriptor-template GDS layer — GDS_LEIDEN, GDS_LOUVAIN, GDS_PPR Aug 13, 2026
@zachwinter

Copy link
Copy Markdown
Contributor Author

Both addressed in d9ece89 (+d90630c). Boilerplate: descriptor templates per algorithm shape — the per-node-scalar template now serves PAGE_RANK, LOUVAIN, LEIDEN, and PPR; a new algorithm in this class is a ~30–100 line descriptor. Leiden: GDS_LEIDEN via ParallelLeidenView with gamma/iterations params, first rider on the template. PPR came along as the extra-args policy's proof (sources = teleportation anchors — the trust primitive from the Discord thread). 78/78.

@adsharma
adsharma merged commit 16c357a into LadybugDB:main Aug 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants