Build a CKKS circuit¶
Build one reusable encrypted computation, run it on local test parameters, and read the decoded result.
Before you start¶
Complete local setup. To run this program yourself, create ckks_circuit.py from the Python example in the next section, then run:
The example uses CKKSParams.python_test(). These parameters are fast and not secure.
Define and run the circuit¶
from ren.schemes.ckks import CKKSParams, Ciphertext, decode, decrypt, encode, encrypt, keygen
def score(features: Ciphertext) -> Ciphertext:
neighboring_sum = features + features.rotate(1)
return (neighboring_sum * 0.25) + 0.5
params = CKKSParams.python_test()
ctx = keygen(params)
inputs = [1.0, 2.0, 3.0, 4.0]
with ctx:
encrypted = encrypt(encode(inputs, msg_bound=4))
output = score(encrypted)
decoded = decode(decrypt(output), length=len(inputs))
level = output.level
print("level:", level)
print("slots:", [round(z.real, 3) for z in decoded])
level: 4
slots: [1.25, 1.75, 2.25, 1.5]
The score(...) function is the circuit boundary. It accepts a Ciphertext, rotates its packed slots, combines neighboring values, scales the result, and returns another Ciphertext.
The call to encrypt(encode(...)) prepares encrypted inputs. The call to decode(decrypt(...)) is only for local inspection after the encrypted computation finishes.
Read the output¶
The printed level shows that multiplying by the non-integer scalar consumed one CKKS level. Integer scalar multiplication does not rescale, but multiplication by a floating-point or complex scalar encodes a plaintext and follows the CKKS scale schedule.
The printed slots are the first decoded slots. encode([1, 2, 3, 4]) stores [1, 2, 3, 4, 0, ..., 0] in the full CKKS slot vector. features.rotate(1) rotates that full vector left, producing [2, 3, 4, 0, ..., 1]; the 1 wraps to the final physical slot. The first four neighbor sums are therefore [3, 5, 7, 4], and the circuit prints sum * 0.25 + 0.5 as [1.25, 1.75, 2.25, 1.5]. Small differences from exact arithmetic are expected in CKKS.
Keep in mind¶
- Choose
msg_boundfrom public input limits, not from secret values. - Generate rotation keys for the slot rotations your circuit uses.
- Keep reusable circuit code separate from local encode, encrypt, decrypt, and decode code.
Next¶
- CKKS workflow shows more CKKS operations and patterns.
ren.jitreplay shows how to replay a stable circuit over matching encrypted inputs.- Execution pipeline explains how this circuit becomes polynomial backend work.