Record where a ciphertext argument starts on the modulus chain - #3357
Open
AlexanderViand wants to merge 1 commit into
Open
Record where a ciphertext argument starts on the modulus chain#3357AlexanderViand wants to merge 1 commit into
AlexanderViand wants to merge 1 commit into
Conversation
A ciphertext argument does not have to start at the top of the modulus chain. HEIR asks the client to encrypt at the level the computation actually begins at, which for a model whose first op is a linear transform is well below the top. On tcn the entry ciphertext is fresh but sits at level 2 of an eleven-modulus chain: func.func @tcn(%0: !lwe.lwe_ciphertext<..., modulus_chain = <elements = <11 moduli>, current = 2>>, ...) The lattigo lowering then gives every ciphertext the same opaque !lattigo.rlwe.ciphertext, and LevelAnalysis::setToEntryState joins every entry lattice to LevelState(0) -- the top of the chain. Everything derived from that argument is believed eight levels shallower than it is. Nothing verifies that belief. lattigo-alloc-to-inplace is the only pass that runs LevelAnalysis after this lowering, and it compares the believed level of a candidate buffer against the believed level of an op's result to decide whether reuse is safe, so a wrong belief costs reuse it should have taken or takes reuse it should have refused. On tcn this changes 34 reuse decisions. Record the starting level while the LWE type still carries it, and have setToEntryState read it back. `current` indexes the module's Q chain, so the depth is measured against the length of that chain from the scheme parameters rather than against the type's own element list: a lowered LWE type can carry a truncated view of the chain -- a fully consumed value in the same function arrives as `elements = <1 modulus>, current = 0` -- and measuring against a truncated list silently reports depth 0. Modules without scheme parameters (hand-written test IR) still fall back to the type's own list. Note this restores information the lowering drops; it is not what keeps the backend from truncating a result. Lattigo resolves an in-place result to min(operand levels, receiver level), so soundness there comes from the emitter giving the receiver the operand's level, not from the level analysis being right.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A ciphertext argument does not have to start at the top of the modulus chain. HEIR asks the client to encrypt at the level the computation actually begins at, which for a model whose first op is a linear transform can be well below the top. (e.g.,
func.func @foo(%0: !lwe.lwe_ciphertext<..., modulus_chain = <elements = <11 moduli>, current = 2>>, ...)The lattigo lowering then gives every ciphertext the same opaque
!lattigo.rlwe.ciphertext, andLevelAnalysis::setToEntryStatejoins every entry lattice toLevelState(0)(top of the chain).As a result, the level analysis used by
--lattigo-alloc-to-inplaceis off, which causes incorrect (or, at least inefficient) buffer reuse.This PR simply adds a
lwe.entry_level_depthattribute to function arguments while still on the LWE types and uses that to determine the level at the! lattigo.rlwe.ciphertextlevel.