CKKS bootstrap internals
This page specifies the mathematical, value-state, and actual-scale invariants of the built-in full-slot composition executed through fhelium.eager.Engine.
Evaluator stack
fhelium.experimental.bootstrap compiles diagonal linear maps and polynomial approximations, then evaluates those stages through the ordinary fhelium.eager.Engine, Ciphertext, NTT, and native-operator stack.
fhelium.experimental.bootstrap.presets constructs versioned compositions. Compiler and evaluator objects are paired by protocol: a compiler's stage representation must be understood by its matching evaluator, and their required_depths reports must match execution. Compiled diagonals, rotation decompositions, and ModRaise constants are Python/runtime resources; the dense arithmetic reaches fhelium_rns_ops, fhelium_ntt_ops, and fhelium_ckks_ops through the engine.
The source tree follows those responsibilities:
full_slot.pyowns the complete entry → ModRaise → CoeffsToSlots → periodic reduction → SlotsToCoeffs composition, its key requirements, and cache lifetime;arithmetic.pydefinesBootstrapArithmetic, which owns one Engine, the prepared materials, and the caller-selected representation-retention mode used by Bootstrap's depth-dependent arithmetic;linear/separates the diagonal value, direct/BSGS evaluators, radix-2 compiler, and key-aware execution of compiled stages;polynomial/separates approximation values and interpolation from power- basis and Chebyshev evaluation schedules;reduction/owns the cosine and exponential periodic functions;structural.pyowns the structural-base transition and centered ModRaise;presets/contains only caller-visible circuit assemblies.
Component evaluate() methods receive a BootstrapArithmetic instance. The same owner is passed through nested periodic and polynomial evaluation. Its cache retains prepared plaintext materials for reuse across those calls.
Notation
Let:
and ; beconfig.default_scale; beengine.max_depth; be the entry Q group and ; be the product of the terminal Q group, used for centered ModRaise; be the ordered Q basis recorded by a ciphertext'sprime_idsat depth ; and be the unscaled CoeffsToSlots and SlotsToCoeffs maps; bemodular_reduction.input_bound; bemodular_reduction.fused_input_divisor, either or for the built-in reducers.
The radix-2 compiler convention is
A plaintext reference round trip therefore compiles scale=1.0 and scale=1.0 / S.
Entry and structural-base transition
The input is a two-component coefficient-domain, standard-residue Q ciphertext at bootstrap.input_depth, which is max_depth - 1. Its dense tensor has axes [component, *batch, limb, coefficient], coefficient extent prime_ids. At that depth the Q basis contains the rows of
Entry preparation chooses an integer
Multiplying both the ciphertext residues and scale by
The fixed transform circuit operates on the coordinate
Arithmetic scale schedule
BootstrapArithmetic.target_scales holds depth-dependent targets
Thus a ciphertext product at scale
Independent polynomial basis nodes use an arithmetic view anchored at the input's actual scale,
A basis scale can become too small after repeated products, requiring a scalar plaintext coefficient beyond the signed-integer encoding range. Material preparation reports that range failure instead of allowing integer overflow. A caller can first use arithmetic.advance_depth(input) to place the value on the arithmetic target schedule, accounting for that additional transition. The evaluator does not silently consume an extra depth.
The terminal depth remains part of the general CKKS chain. Bootstrap owns this entry requirement and uses an ordinary Engine rescale to reach it.
Centered ModRaise
For every ciphertext component and polynomial coefficient, centered ModRaise chooses the unique source representative
consistent with the residue modulo modulus_raise_target_depth. It is a component-wise centered basis extension, not rescale or modulus restriction.
The transition is
depth D=max_depth, prime_ids G_D, coefficient, standard, Q, two components, scale Delta_0
->
depth ell_r, prime_ids Q_ell_r, coefficient, standard, Q, two components,
scale Delta_02
3
4
Data axes and batch shape are preserved; the limb extent changes from len(G_D) to len(Q_ell_r). Production execution uses mixed-radix native operators and currently supports at most eight source rows. The slow reference_centered_basis_extend() oracle reconstructs with Python integers and returns a tensor shaped [*batch, target_limb, coefficient] in standard residues on the input device.
ModRaisedCiphertext privately records the source depth, source prime_ids, source modulus width, and scale. _apply_modraised_linear() requires that provenance for the first linear map and then returns a core Ciphertext. This prevents a centered-raised value from being mistaken for an unrelated public Q ciphertext.
Cyclic-diagonal linear maps
A DiagonalLinearTransform stores one CPU complex128 vector [slot] for each signed rotation offset
where Rot_k matches numpy.roll(x, k). reference(values) accepts exactly one vector of shape [slot], returns the same shape, and performs neither CKKS encoding nor scale or depth simulation.
The direct evaluator rotates, plaintext-multiplies, and sums all diagonal terms, then rescales once. BSGS writes
BSGS rescaling of each giant-group accumulator is algebraically equivalent to the direct sum's single rescale because all group terms share the same pending scale. The schedules have the same map and state transition, but different rounding order can prevent bitwise equality.
DiagonalBSGSEvaluator.aggregate_groups selects a represented grouped route when all direct baby-step keys are present. ckks.GroupedRotationWeightedSumOp records the supplied baby rotations and computes rns.MontgomeryWeightedSumsOp, which accumulates all plaintext groups without materializing a term-product axis. The evaluator retains ownership of BSGS partitioning and giant rotations.
For either evaluator, diagonal plaintexts are unbatched [limb, ntt_index] tensors in NTT/Montgomery form over the active Q basis. They broadcast over ciphertext batch axes. At depth
CoeffsToSlots, branch split, and coordinates
Construction compiles numerical factors
The factors multiply diagonal values; they are distinct from the diagonal plaintext's encoding scale selected by BootstrapArithmetic. Let
After the compiled CoeffsToSlots stages, _multiply_scalar(..., 1 / S) consumes one depth and retains the actual scale selected by the arithmetic schedule. The represented complex coordinate is
Conjugation gives
- if normalization is not fused,
and the reducers receive raw coordinates and ; - if normalization is fused,
and they receive normalized coordinates and .
Conjugation, branch addition/subtraction, and monomial multiplication preserve depth, actual scale, two components, Q basis, domain, residue representation, and prime_ids.
Periodic reduction
The raw coordinate is
Both built-in reducers target
For
The method inputs deliberately differ:
reference(values)always consumes normalized and applies the fitted polynomial and recurrence directly;- non-fused
evaluate(...)consumes raw and spends one scalar-multiply depth computing ; - fused
evaluate(...)assumes the caller already supplied .
Cosine recurrence
For double_angle_iterations, the fitted seed is
followed by
The final value approximates
Exponential recurrence
The power-basis seed truncates
For
The output of each built-in reduction is a functional two-component, coefficient-domain standard-RNS Q ciphertext at input depth plus required_depths, unchanged batch shape, and the corresponding active prime_ids. Its actual scale follows the arithmetic schedule and quotient scales; it need not equal
Polynomial basis and evaluator requirements
PolynomialApproximation.coefficients is always ascending degree:
ChebyshevInterpolator maps a physical coordinate
Its returned coefficients are functions of domain records evaluate_plaintext() and the homomorphic evaluators do not apply this affine normalization. The caller must provide the basis coordinate and account for any depth required to compute it.
BalancedPowerEvaluator uses shared balanced powers. BinaryDecompositionChebyshevEvaluator uses
Both consume a two-component coefficient-domain standard-RNS Q ciphertext and return the same state at the declared deeper depth. Ciphertext products temporarily enter NTT/Montgomery form, produce three components, relinearize to two components, then divide actual scale by the group product during rescale.
Recombination, SlotsToCoeffs, and output
After periodic reduction, multiplication of the imaginary result by
The fixed SlotsToCoeffs factor
The output depth is
where modular_reduction.required_depths. The output is a functional two-component coefficient-domain standard-RNS Q ciphertext with unchanged batch axes and Q_ell_out prime_ids.
Primitive keys, caches, and factory requirements
required_rotations is the union of direct or BSGS transform offsets. key_steps("direct") returns that inventory. key_steps("power_of_two") returns signed-power components that _rotate_with_key_inventory() composes online. create_rotation_keys() generates only the selected RotationKeySet. The callable accepts one EvaluationKeySet through evaluation_keys=; that set contains the rotation inventory, the required conjugation key, and a relinearization key when the selected reduction performs ciphertext products. Built-in reductions reject a missing relinearization key; a custom slotwise reduction without ciphertext products may omit it. Branch splitting always requires conjugation, and the same primitive is passed to the reduction for algorithms such as exponential sine extraction. The replaceable reduction stage is slotwise and does not receive or add rotation-key requirements.
The callable's optional diagonal cache retains prepared plaintexts, its constant cache retains prepared scalar and monomial materials, its rotation cache contains integer decompositions, and its ModRaise cache contains modulus-dependent arithmetic tables. clear_cache() releases all of these callable-owned caches. None is part of ciphertext identity or serialized arithmetic state.
The versioned logn16 factories document a configuration derived from Preset.slots32768_scale50_depth27_int64 for a CkksConfig configured with galois_generator=5. Construction enforces only:
- a valid target depth;
;- nonempty compiled transforms with the engine's slot count;
- sufficient public Q depth for the declared component costs.
The application must establish the input-range bound, validate numerical accuracy, and define workload benchmark acceptance criteria. Construction does not verify derivation from the documented preset baseline or certify those workload properties.