Adds Fourier-Extension Functional Bootstrapping - #1276
Conversation
Update to v1.3.0
Update to v1.3.1
Updates to v1.4
Updates to v1.4.1
Updates to v1.4.2
Updates to v1.5.0
Updated documentation in security.rs for v1.5.0
Updates to v1.5.1
Added funding acknowledgment for ARPA-H research.
- populate scipy coefficient generation repo - move coefficients from header to examples/unit tests - add identity function baseline coefficients - implement GetFEFBTDepth based on polynomial degree - switch K_SPARSE_ALT to K_SPARSE - add LT mode - make lEnc/lDec configurable, remove hardcode - add FLEXIBLEAUTO* support
|
The below discussion compares the FLEXIBLEAUTO implementation with the one in the issue-1218 branch. This will be addressed after issue-1218 is merged to FLEXIBLEAUTO handling: comparison with the issue-1218 FBT designI compared the FLEXIBLE* support here against the approach recently added for Side by sideIssue-1218 (
This PR (
Why the bare declaration still passes the tests: unlike FBT, which imports an external RLWE ciphertext at an arbitrary Suggested changes
One thing that does not need changing: the AUTOEXT one-level-deeper handling — |
- Clarify FEFBT identity coefficient declaration - Remove obsolete composite-scaling code from AdjustCiphertextFEFBT - Correct FEFBT setup guidance in precomputation errors - Reject FEFBT setup on unsupported 128-bit builds - Remove the unused chrono header
Fourier-Extension Functional Bootstrapping (FEFBT)
Documentation for PR #1276 — Adds Fourier-Extension Functional Bootstrapping (branch
issue1207-ckks-fefbt→dev), covering the two feature commits by FYHSSGSS:fff53d678fc52ae2FLEXIBLEAUTO*support, configurablelEnc/lDec, depth formula, coefficient reorganization1. What this feature is
Standard CKKS bootstrapping (
EvalBootstrap) refreshes a ciphertext by homomorphically evaluating the identity function — its only job is to remove theq·Ioverflow left by raising the modulus, via a sine/cosine approximation of modular reduction.Functional bootstrapping evaluates an arbitrary function f during the refresh, at essentially the cost of one bootstrap plus one polynomial evaluation. OpenFHE already has one flavor (
EvalFBT, a digit/LUT-style approach for discrete inputs). FEFBT adds a second flavor aimed at real-valued (continuous) functions:The "Fourier extension" part of the name refers to how the coefficients are produced: f is fitted on a sub-interval of the period (the message domain), with the fit free to do whatever it needs on the rest of the period. This gives much faster-converging series than a plain Fourier expansion of a non-periodic function.
Relationship to existing bootstrapping code
The implementation deliberately mirrors
EvalBootstrapStCFirst(slots-to-coefficients before modulus raising) and reuses the existing machinery:CKKSBootstrapPrecom,EvalCoeffsToSlots/EvalSlotsToCoeffs, the linear-transform (LT) fallback,EvalBootstrapKeyGenfor keys, and the sparse-encapsulation key-switch path.2. Pipeline
flowchart TD A["Input ciphertext (slots encoding, near bottom of chain)"] --> B["SlotsToCoeffs (or LT when levelBudget = {1,1})"] B --> C["Raise modulus to Q0 chain (raw tower-0 reinterpretation)"] C --> D["SPARSE_ENCAPSULATED only: two-step sparse key switch"] D --> E["AdjustCiphertextFEFBT: fix scaling-factor metadata (FLEXIBLE*)"] E --> F["Sparse slots: partial-sum rotations, then scale by 1/(2kN)"] F --> G["CoeffsToSlots + conjugate-add (take real part)"] G --> H["Chebyshev evaluation of exp(2πi·x / 2^r)"] H --> I["r double-angle squarings → z = exp(2πi·x); q·I overflow vanishes by periodicity"] I --> J["EvalPoly: series Σ cⱼ·zʲ with user Fourier coefficients"] J --> K["+ conjugate + 2·c₀ → f(x), real, refreshed ciphertext"]Step-by-step, in
FHECKKSRNS::EvalFEFuncBootstrap(ckksrns-fhe.cpp):StC first. The message is moved to coefficient encoding while still at low level (
EvalSlotsToCoeffs, orEvalLinearTransformin LT mode).Modulus raise. Each polynomial is reduced to its first RNS tower in coefficient format and re-expanded over the full
Q0chain — the standard raw raise, leaving message+ q·I.Sparse encapsulation (if
SecretKeyDist == SPARSE_ENCAPSULATED): key-switch to the sparse key before the raise-sensitive steps, then back (automorphism keys2N−4/2N−2), same as regular bootstrapping.Metadata fix.
AdjustCiphertextFEFBTre-stamps the scaling factor from the canonical level table forFLEXIBLEAUTO/FLEXIBLEAUTOEXT(no-op forFIXED*).Sparse packing. For
slots < N/2, partial sums via rotations replicate the sparse message, and the ciphertext was pre-multiplied by 2 to compensate the folding.Normalization. Multiply by
1/(2kN)— the1/(2N)from the raise convention plus the1/K_UNIFORMrange compression for uniform keys (for sparse keys the1/Kfactor was already folded into the CtS matrix at setup, see §4).CtS + real part.
EvalCoeffsToSlotsfollowed by adding the conjugate.Complex exponential. A Chebyshev series (key-distribution-specific table, §4) evaluates
exp(2πi·x/2^r);rdouble-angle squarings then producez = exp(2πi·x)exactly on all branches of the raised message — this is where the integer overflowIdisappears.Fourier series.
EvalPoly(ctxtExp, coefficients)evaluatesΣⱼ cⱼ·zʲ(withc₀zeroed out and re-added in the clear), then the conjugate is added and2·Re(c₀)restored:result = 2 · Re( Σⱼ₌₀..d cⱼ · e^(2πi·j·x) )
The user's coefficient vector must therefore satisfy
f(x) = 2·Re(Σ cⱼ e^(2πi·j·x))on the message domain (see §5 for the convention used by the shipped tables).3. Public API
Two methods are added at all three layers (
CryptoContextImpl,SchemeBase/FHEBasevirtuals,FHECKKSRNSoverrides):EvalFEFuncBootstrapSetup(levelBudget, dim1, slots)— precomputes the StC/CtS (or LT) matrices and stores them in the sharedm_bootPrecomMap[slots]withBTSlotsEncoding = true.levelBudget = {enc, dec}are the CtS/StC level budgets;{1, 1}selects the single-linear-transform (LT) mode added in the second commit.dim1is the usual baby-step/giant-step dimension hint;slots = 0means full packing (N/2).EvalFEFuncBootstrap(ct, coefficients)— performs the pipeline of §2 and returns a refreshed ciphertext encryptingf(message).coefficientsare the one-sided Fourier(-extension) coefficients of f.EvalBootstrapKeyGen(secretKey, slots)after setup (rotation, conjugation, and sparse-encapsulation keys are shared with regular bootstrapping).A static depth helper is added to
FHECKKSRNS(templated forint64_tandcomplex<double>coefficient vectors):which computes
The example adds
+6levels of headroom on top of this for post-bootstrap computation and encoding margin.4. Supported configurations
HYBRIDonlyFIXEDMANUAL,FIXEDAUTO,FLEXIBLEAUTO,FLEXIBLEAUTOEXTFLEXIBLEAUTO*added by the second commit;FLEXIBLEAUTOEXThandled via an extra StC level and anL0 − 1adjustmentSPARSE_TERNARY,SPARSE_ENCAPSULATED,UNIFORM_TERNARYNATIVE_SIZE = 64onlyEvalFEFuncBootstrapthrows unconditionally on 128-bit builds; unit tests are compiled out forNATIVEINT == 128N/2) and sparseCOMPLEXCKKS data type in the examplePer-key-distribution constants (class-scope tables in
ckksrns-fhe.h; naming iscoeff_exp_<K>_double_<degree>— approximation range[−K, K], then double-angle, Chebyshev degree):rSPARSE_TERNARYK_SPARSE)coeff_exp_28_double_48R_func_28_double_48 = 3SPARSE_ENCAPSULATEDK_SPARSE_ENCAPSULATED)coeff_exp_16_double_23R_func_16_double_23 = 4UNIFORM_TERNARYK_UNIFORM)coeff_exp_512_double_23R_func_512_double_23 = 9The second commit switched the sparse path from
K_SPARSE_ALT = 25to the standardK_SPARSE = 28, aligning FEFBT with the constants used by regular bootstrapping.At setup time the encoding/decoding matrices are scaled so the pipeline needs no extra level for range compression:
scaleEnc = pre/kfolds the1/Kfactor into CtS for sparse keys (k = 1for uniform, where the factor is instead applied at runtime in the1/(2kN)multiplication), andscaleDechandles theq/Δcorrection (composite-degree aware). The setup also computes theFLEXIBLEAUTO*correction factor with the same formula asEvalBootstrapSetup.Level placement of inputs. Because StC runs first, the input ciphertext should sit near the bottom of the chain: both the example and the tests encode at
i.e., with exactly
levelBudget[1] + 1levels remaining for StC before the raise.5. Fourier coefficients: convention and provided tables
The runtime computes
2·Re(Σⱼ₌₀..d cⱼ zʲ), so a coefficient vector for a real function f must satisfy:c₀real, equal to half the DC term of the series;j ≥ 1,cⱼis the one-sided coefficient (thec₋ⱼ = conj(cⱼ)half is supplied by the conjugate-add).Domain normalization. The encrypted message lives in
[−0.5, 0.5](one period). A function on[−B, B]is handled by fittingg(t) = f(2Bt)fort ∈ [−0.5, 0.5]— the example builds inputs in[−0.5, 0.5)and compares againstf(2·B·t).Coefficient tables follow the naming pattern
coeff_<function>_<B>_double_<degree>and were moved out of the header by the second commit — the function-specific tables now live where they are used:coeff_identity_1_double_25y = x(baseline)[−0.5, 0.5]ckksrns-fhe.h(shipped baseline)coeff_exp_2_double_29exp(x)[−2, 2]coeff_sigmoid_8_double_341/(1+e^(−x))[−8, 8]coeff_gelu_8_double_44[−8, 8]Per the commit message, the coefficients are generated with a SciPy-based tooling repo (Fourier-extension least-squares fit); the generator itself is not part of this PR.
6. Example
src/pke/examples/FE-functional-bootstrapping-ckks.cppdemonstrates the full flow at production scale:2^16,2^15slots (full packing),FLEXIBLEAUTO, 59-bit scaling / 60-bit first modulus,SPARSE_TERNARY,HYBRIDwith 3 digits,levelBudget = {3, 2};max(GetFEFBTDepth(...))over the three target functions,+6;EvalFEFuncBootstrapcalls on the same input ciphertext (exp, sigmoid, GELU), each reporting total time, amortized per-slot time, sample values, and mean precision in bits.Sketch:
7. Test coverage
src/pke/unittest/utckksrns/UnitTestFEFBT.cpp— parameterized GTest suite (UTCKKSRNS_FEFBT), ring dimension2^12, depth 26, toleranceeps = 1e-4, 14 cases:SPARSE_TERNARY,SPARSE_ENCAPSULATED,UNIFORM_TERNARYN/2 = 2048) and sparse (8)FIXEDMANUAL,FIXEDAUTO,FLEXIBLEAUTO,FLEXIBLEAUTOEXT{3, 2}and LT mode{1, 1}FEFBT_ACCURACY(decrypt-and-compare) andFEFBT_POST_ROTATION(rotations on the refreshed ciphertext still work)The whole suite is compiled out on
NATIVEINT == 128builds.8. What each commit contributed
fff53d67— "Add Fourier-Extension CKKS functional bootstrapping"introduced the complete vertical slice: the two API methods through
cryptocontext.h→base-scheme.h→base-fhe.h→FHECKKSRNS, the ~280-line CKKS implementation, the example, and the first unit tests.8fc52ae2— "feat: complete majority of FEFBT implementation"levelBudget = {1, 1}now builds single linear-transform matrices (EvalLinearTransformPrecompute/EvalLinearTransform) instead of the collapsed-FFT decomposition.FLEXIBLEAUTO/FLEXIBLEAUTOEXTsupport: correction-factor computation in setup,AdjustCiphertextFEFBTmetadata fix,L0/lDecadjustments for theAUTOEXTextra level.lEnc/lDec: encoding levels for the StC/CtS matrices are computed fromL0, the level budgets, and the scaling technique instead of being hardcoded;lvlbis now serialized inCKKSBootstrapPrecom.GetFEFBTDepthimplemented from the actual polynomial degrees (GetMultiplicativeDepthByCoeffVector) instead of a placeholder.K_SPARSE_ALT → K_SPARSE(25 → 28), matching regular bootstrapping.coeff_identity_1_double_25added.EvalFBTSetupInternalnow computesL0,lEnc,lDecwithFLEXIBLEAUTOEXT-aware offsets — a behavioral change for the pre-existingEvalFBTfeature that deserves its own regression check.9. Observations for review
These are notes from reading the diff, not part of the feature description:
coeff_identity_1_double_25sits at namespace scope inckksrns-fhe.h, after theFHECKKSRNSclass closes, but with member-style indentation and astaticqualifier (internal linkage — a private copy in every translation unit that includes the header). It looks like it was meant to be a class member alongside the other tables.AdjustCiphertextFEFBT: a ~20-line commented-out alternative implementation (composite-scaling handling) is left in the body. Also, the active body only handlesFLEXIBLE*;COMPOSITESCALING*is unaddressed.EvalFEFuncBootstraptells the user to callEvalBootstrapSetup/EvalBootstrapKeyGenrather thanEvalFEFuncBootstrapSetup. A nearby exception also uses the abbreviation "FEFBS" while everything else says FEFBT.EvalFEFuncBootstrapSetuppermits 128-bit builds underFIXED*scaling, butEvalFEFuncBootstrapthrows unconditionally onNATIVEINT == 128— the setup guard could reject earlier.m_bootPrecomMap[slots]used byEvalBootstrapSetup; configuring both regular bootstrapping and FEFBT for the same slot count on one context silently overwrites one with the other.std::vector<std::complex<double>>is copied through all four layers on every call (the mutation ofcoefficients[0]justifies one copy at the innermost layer, not four).#include <chrono>was added tockksrns-fhe.cppbut the timing code uses the existingTimeVar/TIC/TOC; the include appears unnecessary.EvalFBTSetupInternalchange overlaps the recent FLEXIBLE* FBT support; worth running the FBT unit tests withFLEXIBLEAUTOEXTon this branch.