Huelse / SEAL-Python

Microsoft SEAL 4.X For Python
MIT License
321 stars 65 forks source link

encrypted1 and encrypted2 parameter mismatch #107

Closed Ritian-Li closed 1 year ago

Ritian-Li commented 1 year ago

Hi, I am trying to use CKKS to calculate a series of mathematical operations. I noticed that after performing multiplication operations, a relinear operation is required, and at the same time, a rescale operation is required between multiplication and addition calculations. I successfully scaled the scale after multiplication, but the following error occurred during the subtraction operation. How can I obtain the correct calculation results

Descripe

mode = "ckks"
params.set_scheme(mode)
params.set_poly_degree(32768)
params.set_coeff([60, 40, 40, 60])
params.set_plain(60)

context = params.getContext()
core = Calcore(context, mode, scale=4)
privateKey = core.getPrivateKey()
publicKey = core.getPulicKey()
relinKey = core.getRelinearKey()

def rescale(self, value1, value2):
    value1 = self.evaluator.rescale_to_next(value1)
    value1 = self.evaluator.mod_switch_to(value2, value1.parms_id())
    return value1

def relinear(self, value: Ciphertext):
    return self.evaluator.relinearize(value, self.relin_key)

data1 = 2
data2 = 3

enc1 = core.encrypt(data1)
enc2 = core.encrypt(data2)
print("3 - 2 = 1")
res1 = core.sub(enc2, enc1)

print("1 * 3 = 3")
res2 = core.mul(res1, enc2)
res2 = core.relinear(res2)

print("3 * 2 = 6")
res3 = core.mul(res2, enc1)
print("Before rescale", math.log2(res3.scale()))
res3 = core.relinear(res3)
res3 = core.rescale(res3, enc2)
print("After rescale", math.log2(res3.scale()))

print("6 - 3 = 3")
res4 = core.sub(res3, enc2)

Here is the log:

3 - 2 = 1
1 * 3 = 3
3 * 2 = 6
Before rescale 120.0
After rescale 40.0
6 - 3 = 3
Traceback (most recent call last):
  File "test.py", line 54, in <module>
    res4 = core.sub(res3, enc2)
  File "/data/encrypt.py", line 380, in sub
    res = self.evaluator.sub(value1, value2)
ValueError: encrypted1 and encrypted2 parameter mismatch
Huelse commented 1 year ago

The function rescale is a little confusing, I tried your code:

parms = EncryptionParameters(scheme_type.ckks)
poly_modulus_degree = 8192
parms.set_poly_modulus_degree(poly_modulus_degree)
parms.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, [ 60, 40, 40, 60 ]))
scale = 2.0 ** 40

context = SEALContext(parms)
ckks_encoder = CKKSEncoder(context)
slot_count = ckks_encoder.slot_count()

print_parameters(context)

keygen = KeyGenerator(context)
public_key = keygen.create_public_key()
secret_key = keygen.secret_key()
relin_keys = keygen.create_relin_keys()
gal_keys = keygen.create_galois_keys()

encryptor = Encryptor(context, public_key)
evaluator = Evaluator(context)
decryptor = Decryptor(context, secret_key)

print(f'slot_count: {slot_count}')
data = [2.] * slot_count
plain = ckks_encoder.encode(data, scale)
cipher = encryptor.encrypt(plain)

data1 = [3.] * slot_count
plain1 = ckks_encoder.encode(data1, scale)
cipher1 = encryptor.encrypt(plain1)

r1 = evaluator.sub(cipher1, cipher)

r2 = evaluator.multiply(r1, cipher1)
r2 = evaluator.relinearize(r2, relin_keys)
r2 = evaluator.rescale_to_next(r2)
cipher = evaluator.mod_switch_to(cipher, r2.parms_id())

r3 = evaluator.multiply(r2, cipher)
r3 = evaluator.relinearize(r3, relin_keys)
r3 = evaluator.rescale_to_next(r3)
r3.scale(2.0 ** 40)

print(math.log2(r3.scale()))
r4 = evaluator.mod_switch_to(cipher1, r3.parms_id())
print(math.log2(r4.scale()))

res = evaluator.sub(r3, r4)
plain_res = decryptor.decrypt(res)
result = ckks_encoder.decode(plain_res)
print(result)
# [3.00000483 3.00000483 3.00000482 ... 3.00000483 3.00000482 3.00000483]
Ritian-Li commented 1 year ago

Sorry, the code sent before is the result of my encapsulation. The following code can reproduce the problem I mentioned. @Huelse

import math
from seal import *

poly_modulus_degree = 8192
coeff = [60, 40, 40, 60]
scale = 2.0 ** (4*10)

params = EncryptionParameters(scheme_type.ckks)
params.set_poly_modulus_degree(poly_modulus_degree)
params.set_coeff_modulus(CoeffModulus.Create(poly_modulus_degree, coeff))
context = SEALContext(params)

encoder = CKKSEncoder(context)
keygen = KeyGenerator(context)
secret_key = keygen.secret_key()
public_key = keygen.create_public_key()
relin_key = keygen.create_relin_keys()
encryptor = Encryptor(context, public_key)
decryptor = Decryptor(context, secret_key)
evaluator = Evaluator(context)
slot_count = encoder.slot_count()

data1 = 2.
data2 = 3.

enc1 = encryptor.encrypt(encoder.encode([data1] * slot_count, scale))
enc2 = encryptor.encrypt(encoder.encode([data2] * slot_count, scale))

print("3 - 2 = 1")
res1 = evaluator.sub(enc2, enc1)

print("1 * 3 = 3")
res2 = evaluator.multiply(res1, enc2)
res2 = evaluator.relinearize(res2, relin_key)

print("3 * 2 = 6")
res3 = evaluator.multiply(res2, enc1)
print("Before rescale", math.log2(res3.scale()))

res3 = evaluator.relinearize(res3, relin_key)
res3 = evaluator.rescale_to_next(res3)
res3 = evaluator.rescale_to_next(res3)
res3.scale(scale)
res4 = evaluator.mod_switch_to(enc2, res3.parms_id())
print("After rescale", math.log2(res3.scale()))

print("6 - 3 = 3")
res4 = evaluator.sub(res4, enc2)
Ritian-Li commented 1 year ago

And I also tried your code, and it can success run. This makes me very confused

Ritian-Li commented 1 year ago

I found the reason, the result of evaluator.mod_switch_to() is the enc2.

Ritian-Li commented 1 year ago

I'm sorry for encountering some issues again. Based on the code you provided, I can continue to subtract r3 and r2. But if you multiply r3 and r2, an 'scale out of bounds'error will be reported. So I checked their scale, and they are already the initial scale. I would like to know if this situation can be further calculated? It can only be decrypted and re encrypted for calculation. Thank you for your answer!!

print(math.log2(r3.scale()))    # => 40.0
print(math.log2(r2.scale()))    # => 80.0
r2 = evaluator.rescale_to_next(r2)
r2.scale(scale)
r2 = evaluator.mod_switch_to(r2, r3.parms_id())
print(math.log2(r2.scale()))  # => 40.0

# evaluator.sub(r3, r2) # => [3.00000443, 3.00000442, 3.00000443, ..., 3.00000444, 3.00000442, 3.00000442]
evaluator.multiply(r3, r2) # => ValueError: scale out of bounds
Huelse commented 1 year ago

scale out of bounds means your noise is out of budget, need to enlarge the poly_modulus_degree and coeff_modulus. The examples is a good start.

Ritian-Li commented 1 year ago

Thanks for your answer! : )