Scale and level lifecycle
Scale and level are independently tracked coordinates of CKKS value state in FHElium. The application chooses encoding scales and state transitions. Evaluator operations validate their input state and record the resulting scale, level, and active modulus rows.
This page defines the public scale and level coordinates, valid public levels, transition laws, compatibility requirements, and transition queries.
Scale-level transition state
A scale- or level-changing operation is described by the tuple
where:
is the public Q-chain level stored asvalue.level; is the positive finite binary64 actual scale stored asvalue.scale; is the ordered tuplevalue.prime_idsthat maps each dense limb row to one canonical parameter prime; is the modulus basis, either or .
level and scale are independent coordinates. Level selects the active Q suffix, basis independently selects Q or QP, and prime_ids records the resulting RNS rows. Complete operation compatibility also includes context, shape, polynomial domain, residue representation, component count, dtype, and device.
Default scale and actual scale
config.default_scale is the default encoding and planning value
engine.plaintext, engine.encode, and engine.encrypt_message select scale argument is None. Once a value exists, its scale field is the actual scale used by subsequent arithmetic and decoding.
The configured scale primes are close to
FHElium records the binary64 quotient by the actual dropped prime.
All public scale entry points and value constructors require a value that can be represented as a finite Python float and satisfies
NaN, infinity, zero, negative values, booleans, and strings are rejected with InvalidScaleError. A scale multiplication or division that overflows or underflows the finite-positive range is rejected at the operation that produces it.
Level addresses active Q rows
Let a configuration contain
The complete ordinary-prime count is therefore
At public level
In the current canonical layout, a complete Q value has
value.prime_ids == engine.rns_layout.prime_ids(value.level)and a QP value at the same level appends every special P prime ID. level selects the canonical Q suffix, and prime_ids maps each dense limb to its modulus. QP is the auxiliary basis at that level.
engine.public_level_count is engine.final_public_level is [0, engine.final_public_level]. Public rescale_to_next_level and mod_switch_to_next_level require a following public level, so from level
The built-in bootstrap entry consumes the final public level and performs the private transition to [q_b]. Its compiled scale policy is documented in Composable CKKS bootstrap.
Scale-level transition laws
The following laws define multiplication, rescale, modulus switch, and scale reinterpretation.
Multiplication preserves level and multiplies scale
For compatible ciphertext operands,
multiply_plaintext applies the same scale-product law to a ciphertext and an operation-ready plaintext. Both multiplication primitives require and return NTT/Montgomery ciphertext state, preserve the active Q basis, and expose domain transitions separately from scale arithmetic.
Rescale changes level, scale, rows, and payload
At a non-final public level, let rescale_to_next_level computes
and records
For QP input, the leading Q row is removed and every P row is retained. rounding="nearest" and rounding="floor" select different quotient laws but have the same metadata transition.
Modulus switch advances level and preserves scale
For a target public level mod_switch_to_level restricts each residue polynomial to the target active basis:
The operation restricts residues to the target basis while preserving their coefficient representatives. Message preservation requires that the centered represented value remain within the smaller target modulus.
Scale reinterpretation changes metadata and decoded meaning
reinterpret_at_scale(ciphertext, target_scale) leaves every ciphertext residue unchanged and records the requested target scale:
Because decoding divides by the recorded scale, the interpreted message changes according to
Its optional max_relative_change argument bounds the symmetric ratio between the current and target scales. Addition requires scale compatibility before evaluation.
Addition and subtraction require compatible state
Addition and subtraction preserve level and scale, but only after the operands already satisfy their exact compatibility requirements:
with binary64 equality, together with equal context, shape, component count, domain, basis, residue representation, and exact prime IDs. A scale difference of one unit in the last place is a mismatch. add, subtract, sum_ciphertexts, and add_plaintext accept values that already satisfy this set of requirements.
Programs align the two axes separately:
- choose encoding scales and multiplication histories that produce compatible actual scales;
- use
mod_switch_to_levelwhen only the active Q level must advance; - apply guarded
reinterpret_at_scalewhen the resulting message-ratio change is part of the numerical policy.
Scale and level effects by operation family
| Operation family | Level requirements and effects | Scale requirements and effects |
|---|---|---|
plaintext, encode, encrypt_message | Set the requested public level | Set the requested scale; an omitted argument selects config.default_scale |
encrypt, decrypt | Preserve | Preserve |
| RNS, NTT, coefficient-domain, and residue conversions | Preserve | Preserve |
add, subtract, sum_ciphertexts, add_plaintext | Require equality; preserve | Require binary64 equality; preserve |
negate, relinearize, switch_key, rotations, conjugation | Preserve | Preserve |
multiply, multiply_plaintext | Require equality; preserve | Record the product of operand scales |
rescale_to_next_level | Advance by one | Divide by the actual dropped Q prime |
mod_switch_to_next_level | Advance by one | Preserve |
mod_switch_to_level | Set the requested reachable public level | Preserve |
reinterpret_at_scale | Preserve | Replace with the requested scale |
Exact signatures, representation preconditions, exceptions, result allocation, and in-place alias behavior are specified by the generated API reference and method docstrings. Primitive representation, domain, and residue conversions are defined in State transitions and orthogonality. Arithmetic, component-count, and key-dependent effects are described in Evaluator operation transitions.
Query the exact rescale transition
The engine exposes the same level-dependent divisor and binary64 quotient used by rescale_to_next_level:
q_drop = engine.rescale_to_next_drop_prime(level=ciphertext.level)
predicted_scale = engine.rescale_to_next_output_scale(
input_scale=pre_rescale_scale,
level=ciphertext.level,
)
assert predicted_scale == pre_rescale_scale / q_drop2
3
4
5
6
7
These queries return the modulus and scale arithmetic of one transition from an provided source level and input scale.
Binary64 expression ordering is observable. Branches that will be added use a common scale calculation history so their scale metadata satisfies binary64 equality.