Skip to content

Commit 338f2d2

Browse files
authored
Merge pull request #130 from Huelse/update
Update deps and forward wrapper
2 parents a268c9e + 696d9e2 commit 338f2d2

10 files changed

Lines changed: 706 additions & 10 deletions

File tree

AGENTS.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
- `src/wrapper.cpp`: pybind11 bindings that expose Microsoft SEAL C++ APIs as the Python module `seal`.
5+
- `SEAL/`: Microsoft SEAL submodule source and CMake build output (`SEAL/build/...`).
6+
- `pybind11/`: pybind11 submodule headers used by the extension build.
7+
- `examples/`: runnable Python usage samples (for example `4_bgv_basics.py`, `7_serialization.py`).
8+
- Root build files: `setup.py`, `pyproject.toml`, `Dockerfile`, and `README.md`.
9+
10+
## Build, Test, and Development Commands
11+
- `git submodule update --init --recursive`: fetch SEAL and pybind11 submodules.
12+
- `cmake -S SEAL -B SEAL/build -DSEAL_USE_MSGSL=OFF -DSEAL_USE_ZLIB=OFF -DSEAL_USE_ZSTD=OFF && cmake --build SEAL/build`: build static SEAL libraries used by the Python extension.
13+
- `python3 setup.py build_ext -i`: build `seal` extension in-place for local development.
14+
- `python3 setup.py install`: install the module into the active environment.
15+
- `cp seal.*.so examples && python3 examples/4_bgv_basics.py`: smoke-test a Linux/macOS build with an example.
16+
- `docker build -t seal-python -f Dockerfile .`: build reproducible container environment.
17+
18+
## Coding Style & Naming Conventions
19+
- Python: follow PEP 8, 4-space indentation, `snake_case` for functions/variables.
20+
- C++ bindings: keep existing style in `wrapper.cpp` (4-space indentation, grouped bindings by SEAL header/domain).
21+
- Exposed Python symbols should match upstream SEAL naming where practical (for API familiarity).
22+
- Prefer adding small, focused binding blocks rather than large mixed edits.
23+
24+
## Testing Guidelines
25+
- No formal `tests/` suite is currently checked in; use example scripts as regression checks.
26+
- For binding changes, run at least one arithmetic flow (`examples/4_bgv_basics.py`) and one serialization flow (`examples/7_serialization.py`).
27+
- If adding new behavior, include a minimal runnable example in `examples/` named after the feature.
28+
29+
## Commit & Pull Request Guidelines
30+
- Recent history favors short imperative subjects (for example: `Update deps`, `Update README.md`, `Update SEAL`).
31+
- Keep commit titles under ~72 characters and focused on one change.
32+
- PRs should include: purpose, build/test commands run, platform used, and any API surface changes.
33+
- Link related issues and include sample output when behavior changes are user-visible.

examples/1_bfv_basics.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from seal import *
2+
from seal_helper import print_example_banner, print_parameters
3+
4+
5+
def bfv_basics():
6+
print_example_banner("Example: BFV Basics")
7+
8+
parms = EncryptionParameters(scheme_type.bfv)
9+
poly_modulus_degree = 8192
10+
parms.set_poly_modulus_degree(poly_modulus_degree)
11+
parms.set_coeff_modulus(CoeffModulus.BFVDefault(poly_modulus_degree))
12+
parms.set_plain_modulus(1024)
13+
14+
context = SEALContext(parms)
15+
print_parameters(context)
16+
print(f"parameter validation: {context.parameter_error_message()}")
17+
18+
keygen = KeyGenerator(context)
19+
secret_key = keygen.secret_key()
20+
public_key = keygen.create_public_key()
21+
relin_keys = keygen.create_relin_keys()
22+
23+
encryptor = Encryptor(context, public_key)
24+
evaluator = Evaluator(context)
25+
decryptor = Decryptor(context, secret_key)
26+
27+
x = 6
28+
x_plain = Plaintext(str(x))
29+
x_encrypted = encryptor.encrypt(x_plain)
30+
print(f"fresh ciphertext size: {x_encrypted.size()}")
31+
print(f"fresh noise budget: {decryptor.invariant_noise_budget(x_encrypted)} bits")
32+
33+
x_squared = evaluator.square(x_encrypted)
34+
evaluator.relinearize_inplace(x_squared, relin_keys)
35+
36+
one = Plaintext("1")
37+
x_sq_plus_one = evaluator.add_plain(x_squared, one)
38+
39+
x_plus_one = evaluator.add_plain(x_encrypted, one)
40+
x_plus_one_sq = evaluator.square(x_plus_one)
41+
evaluator.relinearize_inplace(x_plus_one_sq, relin_keys)
42+
43+
four = Plaintext("4")
44+
evaluator.multiply_plain_inplace(x_sq_plus_one, four)
45+
encrypted_result = evaluator.multiply(x_sq_plus_one, x_plus_one_sq)
46+
evaluator.relinearize_inplace(encrypted_result, relin_keys)
47+
48+
plain_result = decryptor.decrypt(encrypted_result)
49+
print("decrypted 4(x^2+1)(x+1)^2 (hex):", plain_result.to_string())
50+
print(f"result noise budget: {decryptor.invariant_noise_budget(encrypted_result)} bits")
51+
52+
53+
if __name__ == "__main__":
54+
bfv_basics()

examples/2_encoders.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import numpy as np
2+
from seal import *
3+
from seal_helper import print_example_banner, print_parameters, print_vector
4+
5+
6+
def bfv_batch_encoder_example():
7+
print_example_banner("Example: Encoders / BFV Batching")
8+
9+
parms = EncryptionParameters(scheme_type.bfv)
10+
poly_modulus_degree = 8192
11+
parms.set_poly_modulus_degree(poly_modulus_degree)
12+
parms.set_coeff_modulus(CoeffModulus.BFVDefault(poly_modulus_degree))
13+
parms.set_plain_modulus(PlainModulus.Batching(poly_modulus_degree, 20))
14+
15+
context = SEALContext(parms)
16+
print_parameters(context)
17+
18+
keygen = KeyGenerator(context)
19+
encryptor = Encryptor(context, keygen.create_public_key())
20+
evaluator = Evaluator(context)
21+
decryptor = Decryptor(context, keygen.secret_key())
22+
batch_encoder = BatchEncoder(context)
23+
24+
slot_count = batch_encoder.slot_count()
25+
pod_matrix = [0] * slot_count
26+
pod_matrix[0:8] = [0, 1, 2, 3, 4, 5, 6, 7]
27+
28+
plain_matrix = batch_encoder.encode(pod_matrix)
29+
encrypted_matrix = encryptor.encrypt(plain_matrix)
30+
31+
pod_matrix2 = [((i & 1) + 1) for i in range(slot_count)]
32+
plain_matrix2 = batch_encoder.encode(pod_matrix2)
33+
34+
encrypted_matrix = evaluator.add_plain(encrypted_matrix, plain_matrix2)
35+
evaluator.square_inplace(encrypted_matrix)
36+
evaluator.relinearize_inplace(encrypted_matrix, keygen.create_relin_keys())
37+
38+
plain_result = decryptor.decrypt(encrypted_matrix)
39+
pod_result = batch_encoder.decode(plain_result)
40+
print_vector(pod_result.astype(np.float64), 8, 0)
41+
42+
43+
def ckks_encoder_example():
44+
print_example_banner("Example: Encoders / CKKS")
45+
46+
parms = EncryptionParameters(scheme_type.ckks)
47+
poly_modulus_degree = 8192
48+
parms.set_poly_modulus_degree(poly_modulus_degree)
49+
parms.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, [40, 40, 40, 40, 40]))
50+
context = SEALContext(parms)
51+
52+
encoder = CKKSEncoder(context)
53+
keygen = KeyGenerator(context)
54+
encryptor = Encryptor(context, keygen.create_public_key())
55+
evaluator = Evaluator(context)
56+
decryptor = Decryptor(context, keygen.secret_key())
57+
relin_keys = keygen.create_relin_keys()
58+
59+
input_vec = [0.0, 1.1, 2.2, 3.3]
60+
scale = 2.0 ** 30
61+
plain = encoder.encode(input_vec, scale)
62+
encrypted = encryptor.encrypt(plain)
63+
64+
evaluator.square_inplace(encrypted)
65+
evaluator.relinearize_inplace(encrypted, relin_keys)
66+
67+
output_plain = decryptor.decrypt(encrypted)
68+
output = encoder.decode(output_plain)
69+
print_vector(output, 4, 4)
70+
71+
72+
if __name__ == "__main__":
73+
bfv_batch_encoder_example()
74+
ckks_encoder_example()

examples/3_levels.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from seal import *
2+
from seal_helper import print_example_banner, print_parameters
3+
4+
5+
def levels_example():
6+
print_example_banner("Example: Levels")
7+
8+
parms = EncryptionParameters(scheme_type.bfv)
9+
poly_modulus_degree = 8192
10+
parms.set_poly_modulus_degree(poly_modulus_degree)
11+
parms.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, [50, 30, 30, 50, 50]))
12+
parms.set_plain_modulus(PlainModulus.Batching(poly_modulus_degree, 20))
13+
14+
context = SEALContext(parms)
15+
print_parameters(context)
16+
17+
context_data = context.key_context_data()
18+
print("modulus switching chain:")
19+
while context_data is not None:
20+
print(f" chain index: {context_data.chain_index()}, coeff modulus count: {len(context_data.parms().coeff_modulus())}")
21+
context_data = context_data.next_context_data()
22+
23+
keygen = KeyGenerator(context)
24+
encryptor = Encryptor(context, keygen.create_public_key())
25+
evaluator = Evaluator(context)
26+
decryptor = Decryptor(context, keygen.secret_key())
27+
28+
plain = Plaintext("1x^3 + 2x^2 + 3x^1 + 4")
29+
encrypted = encryptor.encrypt(plain)
30+
31+
print("noise budget while switching levels:")
32+
while True:
33+
print(f" {decryptor.invariant_noise_budget(encrypted)} bits")
34+
try:
35+
evaluator.mod_switch_to_next_inplace(encrypted)
36+
except ValueError:
37+
break
38+
39+
decrypted = decryptor.decrypt(encrypted)
40+
print("decrypted after switching:", decrypted.to_string())
41+
42+
43+
if __name__ == "__main__":
44+
levels_example()

examples/5_ckks_basics.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import math
2+
from seal import *
3+
from seal_helper import print_example_banner, print_parameters, print_vector
4+
5+
6+
def ckks_basics():
7+
print_example_banner("Example: CKKS Basics")
8+
9+
parms = EncryptionParameters(scheme_type.ckks)
10+
poly_modulus_degree = 8192
11+
parms.set_poly_modulus_degree(poly_modulus_degree)
12+
parms.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, [60, 40, 40, 60]))
13+
14+
context = SEALContext(parms)
15+
print_parameters(context)
16+
17+
encoder = CKKSEncoder(context)
18+
keygen = KeyGenerator(context)
19+
encryptor = Encryptor(context, keygen.create_public_key())
20+
evaluator = Evaluator(context)
21+
decryptor = Decryptor(context, keygen.secret_key())
22+
relin_keys = keygen.create_relin_keys()
23+
24+
scale = 2.0 ** 40
25+
input_vec = [0.0, 0.4, 0.8, 1.2]
26+
x_plain = encoder.encode(input_vec, scale)
27+
x1_encrypted = encryptor.encrypt(x_plain)
28+
x2_encrypted = evaluator.square(x1_encrypted)
29+
evaluator.relinearize_inplace(x2_encrypted, relin_keys)
30+
evaluator.rescale_to_next_inplace(x2_encrypted)
31+
32+
plain_coeff3 = encoder.encode(3.14159265, scale)
33+
plain_coeff1 = encoder.encode(0.4, scale)
34+
plain_coeff0 = encoder.encode(1.0, scale)
35+
36+
evaluator.mod_switch_to_inplace(plain_coeff3, x2_encrypted.parms_id())
37+
x3_encrypted = evaluator.multiply_plain(x2_encrypted, plain_coeff3)
38+
evaluator.rescale_to_next_inplace(x3_encrypted)
39+
40+
x1_encrypted_linear = evaluator.multiply_plain(x1_encrypted, plain_coeff1)
41+
evaluator.rescale_to_next_inplace(x1_encrypted_linear)
42+
evaluator.mod_switch_to_inplace(x1_encrypted_linear, x3_encrypted.parms_id())
43+
44+
evaluator.mod_switch_to_inplace(plain_coeff0, x3_encrypted.parms_id())
45+
46+
x3_encrypted.scale(scale)
47+
x1_encrypted_linear.scale(scale)
48+
encrypted_result = evaluator.add(x3_encrypted, x1_encrypted_linear)
49+
encrypted_result = evaluator.add_plain(encrypted_result, plain_coeff0)
50+
51+
plain_result = decryptor.decrypt(encrypted_result)
52+
result = encoder.decode(plain_result)
53+
54+
expected = [3.14159265 * (x * x) + 0.4 * x + 1.0 for x in input_vec]
55+
print("expected:")
56+
print_vector(expected, 4, 6)
57+
print("computed:")
58+
print_vector(result, 4, 6)
59+
60+
61+
if __name__ == "__main__":
62+
ckks_basics()

examples/6_rotation.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from seal import *
2+
from seal_helper import print_example_banner, print_parameters, print_vector
3+
4+
5+
def bfv_rotation():
6+
print_example_banner("Example: Rotation / BFV")
7+
8+
parms = EncryptionParameters(scheme_type.bfv)
9+
poly_modulus_degree = 8192
10+
parms.set_poly_modulus_degree(poly_modulus_degree)
11+
parms.set_coeff_modulus(CoeffModulus.BFVDefault(poly_modulus_degree))
12+
parms.set_plain_modulus(PlainModulus.Batching(poly_modulus_degree, 20))
13+
14+
context = SEALContext(parms)
15+
print_parameters(context)
16+
17+
keygen = KeyGenerator(context)
18+
encryptor = Encryptor(context, keygen.create_public_key())
19+
evaluator = Evaluator(context)
20+
decryptor = Decryptor(context, keygen.secret_key())
21+
batch_encoder = BatchEncoder(context)
22+
galois_keys = keygen.create_galois_keys()
23+
24+
slot_count = batch_encoder.slot_count()
25+
pod_matrix = [0] * slot_count
26+
pod_matrix[0:8] = [0, 1, 2, 3, 4, 5, 6, 7]
27+
28+
plain_matrix = batch_encoder.encode(pod_matrix)
29+
encrypted_matrix = encryptor.encrypt(plain_matrix)
30+
31+
evaluator.rotate_rows_inplace(encrypted_matrix, 3, galois_keys)
32+
print_vector(batch_encoder.decode(decryptor.decrypt(encrypted_matrix)).astype(float), 8, 0)
33+
34+
evaluator.rotate_columns_inplace(encrypted_matrix, galois_keys)
35+
print_vector(batch_encoder.decode(decryptor.decrypt(encrypted_matrix)).astype(float), 8, 0)
36+
37+
38+
def ckks_rotation():
39+
print_example_banner("Example: Rotation / CKKS")
40+
41+
parms = EncryptionParameters(scheme_type.ckks)
42+
poly_modulus_degree = 8192
43+
parms.set_poly_modulus_degree(poly_modulus_degree)
44+
parms.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, [60, 40, 40, 60]))
45+
46+
context = SEALContext(parms)
47+
encoder = CKKSEncoder(context)
48+
keygen = KeyGenerator(context)
49+
encryptor = Encryptor(context, keygen.create_public_key())
50+
evaluator = Evaluator(context)
51+
decryptor = Decryptor(context, keygen.secret_key())
52+
galois_keys = keygen.create_galois_keys()
53+
54+
input_vec = [float(i) for i in range(8)]
55+
plain = encoder.encode(input_vec, 2.0 ** 40)
56+
encrypted = encryptor.encrypt(plain)
57+
58+
rotated = evaluator.rotate_vector(encrypted, 2, galois_keys)
59+
result = encoder.decode(decryptor.decrypt(rotated))
60+
print_vector(result, 8, 3)
61+
62+
63+
if __name__ == "__main__":
64+
bfv_rotation()
65+
ckks_rotation()

0 commit comments

Comments
 (0)