Skip to content

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:

uv run python ckks_circuit.py

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_bound from 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