Skip to content

fix(hora): expand shared nested edges once per path and meter every row - #107

Merged
venkat1701 merged 3 commits into
mainfrom
fix/expand-shared-children
Oct 27, 2026
Merged

venkat1701 merged 3 commits into
mainfrom
fix/expand-shared-children

Conversation

@venkat1701

@venkat1701 venkat1701 commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

EXPAND kept one visited set for the whole traversal. A nested hyperedge reached through two parents (a diamond: top contains A and B, both contain C) was expanded under A and then reported as CYCLE under B, although there is no cycle.

Change

  • The traversal now tracks the hyperedges on the current path. CYCLE only marks a hyperedge that is already an ancestor on that path.
  • Following every path can visit a shared hyperedge many times (a chain of k diamonds has 2^k paths). To keep the output bounded, every emitted row (EMIT, CYCLE, depth limit) and every descent now takes one unit of the row budget. At the first refusal the traversal stops with a single BUDGET_EXHAUSTED row. Before this change only descents were counted, so one wide hyperedge could emit any number of rows.
  • docs/database/hora.md describes the new CYCLE meaning and the budget rule.

LIMIT n on EXPAND now bounds the number of rows to n plus the one marker row.

Tests

  • New DatabaseTest.expandFollowsEveryPathAndStaysWithinItsBudget: diamond (both paths emit the leaf), a real two-edge cycle (CYCLE), a 20-member edge under a budget of 5 (4 rows plus the marker), and 12 stacked diamonds (4096 paths) under a budget of 1000 (at most 1001 rows, one marker).
  • The diamond assertion fails on main (CYCLE instead of the second EMIT).
  • Full build: engine 240, database 71, server 25, all passing.

EXPAND kept one visited set for the whole traversal, so a hyperedge
reached through a second parent was reported as CYCLE. It now tracks
the hyperedges on the current path only, so CYCLE means a real cycle.

Following every path can repeat a shared hyperedge, so every emitted
row and every descent now takes one unit of the row budget, and the
traversal stops with one BUDGET_EXHAUSTED row at the first refusal.
Before, only descents were counted and rows were unbounded.
Rows at the requested DEPTH were labelled BUDGET_EXHAUSTED, so a client
could not tell a complete answer from a cut one. They are now
DEPTH_LIMIT; BUDGET_EXHAUSTED is kept for the row budget and for the
budget's own depth cap.
@venkat1701
venkat1701 merged commit 35cd815 into main Oct 27, 2026
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.

1 participant