Increasing the scale shrinks rounding error while growing the encoded integers and their products.
Error enters at several different layers
round Δz to an integer
add deliberately small noise
multiply values and noise
combine residue limbs
divide by Δ²
An error budget is a rigorous engineering argument that connects those tiny local disturbances to a final, ironclad promise. It should name assumptions, state units, and leave a margin. “CKKS is approximate” is a warning; “for normalized D-dimensional vectors, all tested decoded scores differed from the high-precision reference by at most ε under these parameters” is actual evidence.
Headroom is very different from precision
The coefficient modulus must be massively large enough that the scaled message plus noise does not wrap ambiguously. Precision, however, asks how close the decoded value is to the reference real score. A computation can beautifully avoid overflow yet have terrible precision, or have tiny local rounding error but disastrously exceed its modulus range.
Worked example
A rough integer-growth estimate before encryption
If normalized coordinates have magnitude at most 1, each encoded magnitude is about Δ. One coordinate product is about Δ², and a D-term sum can be as large as roughly DΔ² in a deliberately conservative bound. This is definitely not the complete CKKS noise analysis, but it instantly catches a scale or dimension choice that cannot possibly fit the available range.
Worked example
Measure exactly what the application consumes
For each test vector, compute a high-precision plaintext dot product, run encode/evaluate/decrypt/decode, record absolute and relative error, then check whether your top-k ordering actually changed. Report both the numerical error and the final ranking accuracy.
Experiment before just memorizing parameter names
The client-side notebook lets you safely trade scale bits against error and integer headroom. Try a tiny scale that visibly rounds coordinates, then increase it until the score totally stabilizes. Finally, crank up the coordinate magnitude or dimension and watch why “larger scale is always better” hilariously fails.
Change values and scale bits while tracking rounding error, product scale, integer growth, and remaining headroom.
Runs entirely in this browser. Python executes in Pyodide WebAssembly with no remote kernel. The construction code stays visible while reactive dependents recompute whenever you change an input.
Check your understanding
Why test the integer polynomial identity totally separately from CKKS error?
Section summary
- Encoding, encryption, multiplication, and decoding all affect error.
- Modulus headroom and decoded precision are distinct concepts.
- Layered tests brilliantly isolate failures.
- Top-k stability is the only final application measure that matters.
Repository layer · second pass
How does error move from input rounding to final ranking?
Build an error ledger. Include input quantization, encryption noise, multiplication growth, modulus switching or rescaling, transform arithmetic, decryption, and final floating-point conversion. Some are bounded analytically; others are measured empirically.
Report absolute and relative error together with score margins. A tiny relative error near zero can be misleading, while a small absolute error can still swap a near tie. Separate deterministic encoding error from randomized encryption trials.
Reasoning chain
- 1
Measure plaintext encoding error first.
- 2
Verify exact integer layout identity.
- 3
Add encryption and record trial distribution.
- 4
Track scale and modulus level.
- 5
Decode target taps.
- 6
Compare error with rank margins and thresholds.
Worked trace
Localize before tuning
- Plain integer reference fails.
- Increasing modulus cannot repair an index-layout mistake.
- If integer identity passes but CKKS error is large, inspect scale/noise.
- If scores are accurate but rank differs, inspect margins and ties.
Result. Layered tests prevent parameter tuning from masking logical bugs.
Executable lens · Python
Make the hidden state visible
def error_report(expected, actual):
absolute=[abs(a-b) for a,b in zip(expected,actual)]
return {"max":max(absolute), "mean":sum(absolute)/len(absolute)}Retype this example, predict each intermediate value, and then change one input that touches a boundary.
Misconception clinic
Tempting mistakes
- Using one random trial as a bound.
- Reporting decimal digits without the scale and modulus chain.
Retrieval and transfer
Close the book first
- Design an error-budget table with owner per source.
- Create a near-tie corpus.
- Distinguish correctness tolerance from performance regression thresholds.