Skip to content

feat(algo): add GDS_NODE2VEC (icebug/NetworKit node embeddings) - #47

Merged
adsharma merged 1 commit into
LadybugDB:mainfrom
zachwinter:feat/gds-node2vec
Aug 13, 2026
Merged

feat(algo): add GDS_NODE2VEC (icebug/NetworKit node embeddings)#47
adsharma merged 1 commit into
LadybugDB:mainfrom
zachwinter:feat/gds-node2vec

Conversation

@zachwinter

Copy link
Copy Markdown
Contributor

Summary

Second icebug-backed GDS function, following the pattern #30 established: CALL GDS_NODE2VEC('G') RETURN node, embedding runs NetworKit's Node2Vec over the projected graph and returns a LIST(FLOAT) embedding column (64 dimensions).

projected graph → InMemGraph CSR (same fwd/bwd scan as GDS_PAGE_RANK)
  → NetworKit::GraphR → NetworKit::Node2Vec(P=1, Q=1, walkLength=10, walksPerNode=5, D=64)
  → getFeatures() streamed out per node via ListVector

The bridge half is identical to gds_page_rank.cpp; the new plumbing is the output side — emitting a variable-length list per node through ListVector::addList + the list data vector, rather than a scalar. It builds its CSR with arrow::system_memory_pool() from the start (the mimalloc-under-dlopen issue — see #46).

Why this one next

Embeddings compose with the vector extension: LIST(FLOAT) casts to ARRAY(FLOAT, 64), which feeds CREATE_VECTOR_INDEX / HNSW. That makes GDS_NODE2VEC → cosine / HNSW a graph-native similarity pipeline with no external embedding step.

Testing

gds_node2vec.test: star graph, asserts every node gets a 64-dim embedding. Node2Vec is stochastic (unseeded), so the test asserts shape, not values.

Validated the semantic property separately: on a star graph, structurally-equivalent leaves embed at cosine ~0.997 to each other vs ~0.92 to the hub (via CAST(embedding AS FLOAT[64]) + array_cosine_similarity).

Notes / follow-ups

  • Hyperparameters fixed for now (P=1, Q=1, L=10, N=5, D=64); a config object mirroring page_rank_config.h is the natural follow-up once the GDS surface question (per-algo functions vs a GDS_FUNCTION dispatcher) settles.
  • Single-node-table graphs, same as GDS_PAGE_RANK.

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have added tests.

🤖 Generated with Claude Code

Comment thread algo/src/function/gds_node2vec.cpp
Structural node embeddings from graph topology alone, via NetworKit's Node2Vec —
the graph-native "semantics" primitive: cosine-near embeddings are structurally
(and, for a citation graph, conceptually) related nodes.

Same bridge as GDS_PAGE_RANK (CSR -> NetworKit::GraphR -> algo -> results); the new
piece is a LIST(FLOAT) output column, emitted per node from Node2Vec's getFeatures()
via the ListVector addList pattern. Castable to ARRAY(FLOAT, d) for the vector
extension's HNSW index.

`CALL GDS_NODE2VEC('G') RETURN node, embedding` -> one 64-dim embedding per node.
Validated: on a star graph, the structurally-equivalent leaves have cosine ~0.997
between their embeddings vs ~0.92 to the hub.

MVP: fixed hyperparameters (P=1, Q=1, walk length 10, 5 walks, dim 64) and single
node-table graphs. Configurable params, ARRAY output, and a direct build-HNSW-index
variant are follow-ups. Stacks on the GDS_PAGE_RANK bridge (needs its icebug CMake).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zachwinter

Copy link
Copy Markdown
Contributor Author

Rebased onto main now that #54 is in: this PR is now a single commit — GDS_NODE2VEC consuming the shared buildUndirectedCSR() helper (zero-copy from the projected graph's pinned CSR, scan fallback). Its private CSR/builder code is gone. 74/74 locally. The InMemGraph copy this review flagged is now dead in every GDS function.

@adsharma
adsharma merged commit e103c7e 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