Skip to content

Add complement edges to PacketSetManager (O(1) Not) - #129

Open
matthewtlam wants to merge 1 commit into
google:mainfrom
matthewtlam:feature/packet-set-complement-edges
Open

Add complement edges to PacketSetManager (O(1) Not)#129
matthewtlam wants to merge 1 commit into
google:mainfrom
matthewtlam:feature/packet-set-complement-edges

Conversation

@matthewtlam

Copy link
Copy Markdown
Contributor

Implements the complement-edge (a.k.a. negative/complemented edge) BDD optimization for the packet-set decision diagram, resolving the b/382380335 TODOs on Not and Or.

The top bit of PacketSetHandle::node_index_ becomes a "complement" flag: a handle with the bit set denotes the complement of the set denoted by the same handle with the bit cleared. Consequences:

  • Not is now a single bit flip: O(1), no allocation, no recursion, and the not_cache_ is gone.
  • Only one leaf sentinel is needed (kFullSet); kEmptySet is a complement edge to it.
  • Or/Xor keep their De Morgan formulation but the Nots are free, so they share and_cache_ instead of needing their own memo table.

Canonical form (enforced centrally in NodeToPacket): a stored node's default ("else") edge is never a complement edge; if it would be, the whole node is complemented instead and a complement edge to it is returned. This keeps exactly one node per {set, complement} pair, so "same bits iff same set" (and therefore O(1) operator== / hashing) still holds.

Recursion that descends into a node (And, Exists, Contains, GetConcretePacketsDfs, PacketTransformerManager::FromPacketSetHandle, the pretty-printers) pushes the parent edge's complement bit into the child edges via the new PacketSetManager::ChildEdge helper.

The node-index space halves from ~2^32 to ~2^31 (~2.1B), still far above the "millions, not billions" expectation.

ToDot now emits a single T terminal with complement edges drawn as dotted arrowheads, plus an invisible __root__ entry so a complemented root shows. packet_set_test.expected regenerated; packet_transformer_test.expected is unchanged (transformer behavior is byte-identical).

Tested: bazel test //netkat/... passes in both fastbuild (all debug invariant checks + CheckInternalInvariants active) and -c opt.

Implements the complement-edge (a.k.a. negative/complemented edge) BDD
optimization for the packet-set decision diagram, resolving the b/382380335
TODOs on `Not` and `Or`.

The top bit of `PacketSetHandle::node_index_` becomes a "complement" flag:
a handle with the bit set denotes the complement of the set denoted by the
same handle with the bit cleared. Consequences:

* `Not` is now a single bit flip: O(1), no allocation, no recursion, and the
  `not_cache_` is gone.
* Only one leaf sentinel is needed (`kFullSet`); `kEmptySet` is a complement
  edge to it.
* `Or`/`Xor` keep their De Morgan formulation but the `Not`s are free, so they
  share `and_cache_` instead of needing their own memo table.

Canonical form (enforced centrally in `NodeToPacket`): a stored node's default
("else") edge is never a complement edge; if it would be, the whole node is
complemented instead and a complement edge to it is returned. This keeps
exactly one node per {set, complement} pair, so "same bits iff same set"
(and therefore O(1) `operator==` / hashing) still holds.

Recursion that descends into a node (`And`, `Exists`, `Contains`,
`GetConcretePacketsDfs`, `PacketTransformerManager::FromPacketSetHandle`,
the pretty-printers) pushes the parent edge's complement bit into the child
edges via the new `PacketSetManager::ChildEdge` helper.

The node-index space halves from ~2^32 to ~2^31 (~2.1B), still far above the
"millions, not billions" expectation.

`ToDot` now emits a single `T` terminal with complement edges drawn as dotted
arrowheads, plus an invisible `__root__` entry so a complemented root shows.
`packet_set_test.expected` regenerated; `packet_transformer_test.expected` is
unchanged (transformer behavior is byte-identical).

Tested: `bazel test //netkat/...` passes in both fastbuild (all debug
invariant checks + CheckInternalInvariants active) and -c opt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@google-cla

google-cla Bot commented Sep 4, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

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