Skip to content

Unify rotation entry points and accumulate rotation folds lazily in the extended basis#1225

Open
pascoec wants to merge 3 commits into
devfrom
gpu
Open

Unify rotation entry points and accumulate rotation folds lazily in the extended basis#1225
pascoec wants to merge 3 commits into
devfrom
gpu

Conversation

@pascoec

@pascoec pascoec commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Summary
This PR makes OpenFHE's CKKS rotation-heavy paths — bootstrap EvalPartialSumInPlace, the Horner giant-step in CtS/StC, and the scheme-switching linear transforms — deterministic across entry points and cheaper, by (1) routing every automorphism through one shared core and (2) keeping rotation-fold accumulators in the extended Q·P basis with a single deferred ApproxModDown per fold. It also parameterizes the partial-sum fold radix so the performance/rotation-key-count trade-off is explicit.

Motivation: GPU CKKS backends (specifically FIDESlib) implement rotations in hoisted, lazily-accumulated form. For a GPU backend to produce ciphertexts bit-identical to the CPU reference, the CPU paths must apply the same operation sequence with the same rounding. The changes here achieve that while strictly reducing CPU-side mod-down counts — they are not GPU-specific and stand on their own as a CPU improvement.

Commits

  1. Unification of EvalFastRotation/EvalRotate/EvalAutomorphism/Conjugate
    Adds LeveledSHEBase::EvalAutomorphismCore(ciphertext, autoIndex, digits, evalKey) — the automorphism-plus-key-switch step evaluated from precomputed digits — and delegates EvalFastRotation, EvalAutomorphism (hence EvalRotate/EvalAtIndex), and CKKS Conjugate to it. Previously EvalAutomorphism used a non-hoisted sequence (KeySwitchInPlace, then permute both components) whose rounding differs from the hoisted path; the two entry points produced bit-different results for the same rotation. Now every rotation entry point is bit-identical to EvalFastRotation with fresh digits.

  2. Lazy extended-basis accumulation (deferred ApproxModDown)
    Rotation folds previously settled each key-switched rotation back to the base ring before adding it to the accumulator. Two exact identities make that unnecessary:

adding a base-ring operand into an extended-basis (Q·P) accumulator lifts it by P mod q exactly, and ApproxModDown(P·x + ks) = x + ApproxModDown(ks) — no rounding difference;
automorphisms are coefficient permutations (exact, linear), so they commute with the deferred settle.
So a fold of k rotations can keep the running sum extended and perform one ApproxModDown at the end, bit-identical to the eager form. Applied to:

EvalPartialSumInPlace (bootstrap partial sums) — the accumulator settles once per fold; the c1 component still settles per step, since the next step's digit decomposition consumes it in the base ring;
the Horner giant-step in CoeffsToSlots/SlotsToCoeffs (EvalHornerGiantRotate), including folding the delta corrections in before the final settle;
scheme-switching EvalLTWithPrecomputeSwitch/EvalLTRectWithPrecomputeSwitch (new defaulted ext flag; the sparse SlotsToCoeffs doubling and the wide-matrix log-fold now accumulate extended and settle once).
ApproxModDown is the dominant cost of a rotation after the key-switch products, so per fold this removes all but one of them on the accumulator path.

  1. Radix-parameterized EvalPartialSumInPlace
    Generalizes the partial-sum fold into a radix-r rotation fold and adds two overloads:

EvalPartialSumInPlace(ct, stride, size) — radix-2 specialization, used by all in-tree callers;
EvalPartialSumInPlace(ct, stride, size, radix) — general form (delegates to the 3-arg version when radix == 2).
The trade-off the parameter exposes: mod-ups per fold scale as log2(size)/log2(r), while dedicated rotation keys grow as (r−1)/log2(r) · log2(size). Radix 2 is the default because its rotation indices are all powers of two — shared with the keys bootstrapping already generates — so it minimizes key storage at zero incremental key cost.

@pascoec pascoec added this to the Release 1.6.0 milestone Jul 18, 2026
@pascoec pascoec self-assigned this Jul 18, 2026
@pascoec pascoec added the optimization Improves performance label Jul 18, 2026
@pascoec
pascoec requested a review from yspolyakov July 18, 2026 05:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

optimization Improves performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement CKKS bootstrapping linear transform optimizations from FIDESlib

2 participants