Skip to content

DNF zipper merge#40

Open
marcin-rzeznicki wants to merge 4 commits into
extend_zipper_algfrom
dnf
Open

DNF zipper merge#40
marcin-rzeznicki wants to merge 4 commits into
extend_zipper_algfrom
dnf

Conversation

@marcin-rzeznicki
Copy link
Copy Markdown
Collaborator

This PR introduces a DNF-based zipper merge engine capable of evaluating expressions of the form:

(z11 ∧ z12 ∧ ...)
∨ ...
∨
(zn1 ∧ zn2 ∧ ...)

where each conjunctive clause is evaluated as an intersection (Meet) of zipper frontiers, and the resulting clauses are combined using union (Join).

The implementation extends the existing zipper merge framework while preserving:

  • iterative traversal on unanimous descent paths,
  • bounded recursion on branching points only,
  • structural locality of zipper state,
  • and zero-allocation traversal state.

Representation

The new interface operates on clauses:

fn zipper_merge_dnf<
    V,
    Z,
    Out,
    A,
    const M: usize,
>(
    clauses: &mut [&mut [Z]; M],
    out: &mut Out,
)

Each clause represents a conjunction of zippers:

[
    [A, B],
    [A, C],
]
=
(A ∧ B) ∨ (A ∧ C)

Clause lengths are dynamic while the number of clauses (M) remains const-generic for efficient mask-based dispatch.


Traversal strategy

At each node:

  1. Clause masks are computed as intersections of child masks:
clause_mask = ∧ child_masks
  1. A global frontier is formed as the union of clause masks:
global = ⋃ clause_masks
  1. For each byte in the global frontier:

    • participating clauses are identified,
    • relevant zippers descend,
    • and evaluation continues recursively or iteratively.

Iterative unanimous descent

A key optimization is the detection of unanimous clause continuation:

if sub_active == active

In this case:

  • all currently active clauses descend together,
  • recursion is avoided,
  • and traversal continues iteratively.

This eliminates stack growth on long shared paths and restores the same “tail-descent spine” optimization used by the original mono merge engine.


Recursion behavior

Recursion now occurs only on true branching points:

sub_active ⊂ active

which bounds recursive depth by branching structure rather than trie depth.


State model

Unlike the original zipper merge, DNF traversal introduces aggregate evaluator state:

clause masks
global frontier
active clause sets

Rather than storing this state explicitly across descent, the implementation recomputes it after ascent from zipper positions.

This keeps traversal state lightweight while relying on inexpensive bitmask recomputation.


Performance notes

The implementation preserves several optimizer-friendly properties:

  • M is const-generic,
  • active clause sets are represented as bitmasks,
  • stack allocations are compile-time sized,
  • unanimous descent avoids recursive overhead,
  • and traversal state remains primarily encoded in zipper positions.

The hot path is dominated by compact bitmask operations and zipper movement rather than evaluator bookkeeping.

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