EvalMultMutable and EvalMultMutableInPlace produces wrong results for BFVRNS

Hi, I am using the openfhe-python wrapper and I noticed that when using EvalMultMutable() with BFVRNS the produced result is not correct. I played around a bit and found out that with BGVRNS the function works properly. Additionally, the result stays the same across multiple calls of the function in the same script, but changes when you run the script again.

In the same time I found out that EvalMultMutableInPlace() does not work for either BFVRNS or BGVRNS. Any insight?

Currently I have only tested it in openfhe-python. I am using python-openfhe version 0.8.1 with openfhe version 1.2.0 on Ubuntu 22.04 with gcc version 11.4.0.

I include an example below to reproduce this behavior.

from openfhe import *

# Set CryptoContext and FHE parameters
parameters = CCParamsBFVRNS()

depth = 1
plainmod = 786433
parameters.SetPlaintextModulus(plainmod)
parameters.SetMultiplicativeDepth(depth)
parameters.SetSecurityLevel(HEStd_128_classic)

crypto_context = GenCryptoContext(parameters)

crypto_context.Enable(PKESchemeFeature.PKE)
crypto_context.Enable(PKESchemeFeature.KEYSWITCH)
crypto_context.Enable(PKESchemeFeature.LEVELEDSHE)
crypto_context.Enable(ADVANCEDSHE)

#  Generate a public/private key pair
key_pair = crypto_context.KeyGen()

# Generate the relinearization key
crypto_context.EvalMultKeyGen(key_pair.secretKey)


a = [5]
b = [8]

packed_a = crypto_context.MakePackedPlaintext(a)
packed_b = crypto_context.MakePackedPlaintext(b)

enc_a = crypto_context.Encrypt(key_pair.publicKey, packed_a)
enc_b = crypto_context.Encrypt(key_pair.publicKey, packed_b)

print("a: ", a)
print("b: ", b)

print(f"EvalMultMutable() with a and b with BFV...")

enc_result = []

for i in range(2):
    
    enc_result.append(crypto_context.EvalMultMutable(enc_a, enc_b))

    # Decrypt, set length and unpack
    decrypted_result = crypto_context.Decrypt(enc_result[i], key_pair.secretKey)
    decrypted_result.SetLength(1)
    dec = decrypted_result.GetPackedValue()
    print ("a * b: ", dec)

print(f"EvalMultMutableInPlace() with a and b with BFV...")

enc_result = []

for i in range(2):
    
    crypto_context.EvalMultMutableInPlace(enc_a, enc_b)
    enc_result.append(enc_a)

    # Decrypt, set length and unpack
    decrypted_result = crypto_context.Decrypt(enc_result[i], key_pair.secretKey)
    decrypted_result.SetLength(1)
    dec = decrypted_result.GetPackedValue()
    print ("a * b: ", dec)



# Set CryptoContext and FHE parameters
parameters = CCParamsBGVRNS()

depth = 1
plainmod = 786433
parameters.SetPlaintextModulus(plainmod)
parameters.SetMultiplicativeDepth(depth)
parameters.SetSecurityLevel(HEStd_128_classic)

crypto_context = GenCryptoContext(parameters)

crypto_context.Enable(PKESchemeFeature.PKE)
crypto_context.Enable(PKESchemeFeature.KEYSWITCH)
crypto_context.Enable(PKESchemeFeature.LEVELEDSHE)
crypto_context.Enable(ADVANCEDSHE)

#  Generate a public/private key pair
key_pair = crypto_context.KeyGen()

# Generate the relinearization key
crypto_context.EvalMultKeyGen(key_pair.secretKey)


packed_a = crypto_context.MakePackedPlaintext(a)
packed_b = crypto_context.MakePackedPlaintext(b)

enc_a = crypto_context.Encrypt(key_pair.publicKey, packed_a)
enc_b = crypto_context.Encrypt(key_pair.publicKey, packed_b)


print(f"EvalMultMutable() with a and b with BGV...")

enc_result = []

for i in range(2):
    
    enc_result.append(crypto_context.EvalMultMutable(enc_a, enc_b))

    # Decrypt, set length and unpack
    decrypted_result = crypto_context.Decrypt(enc_result[i], key_pair.secretKey)
    decrypted_result.SetLength(1)
    dec = decrypted_result.GetPackedValue()
    print ("a * b: ", dec)

print(f"EvalMultMutableInPlace() with a and b with BGV...")

enc_result = []

for i in range(2):
    
    crypto_context.EvalMultMutableInPlace(enc_a, enc_b)
    enc_result.append(enc_a)

    # Decrypt, set length and unpack
    decrypted_result = crypto_context.Decrypt(enc_result[i], key_pair.secretKey)
    decrypted_result.SetLength(1)
    dec = decrypted_result.GetPackedValue()
    print ("a * b: ", dec)


and this is a sample output:

a:  [5]
b:  [8]
EvalMultMutable() with a and b with BFV...
a * b:  [77167]
a * b:  [77167]
EvalMultMutableInPlace() with a and b with BFV...
a * b:  [5]
a * b:  [5]
EvalMultMutable() with a and b with BGV...
a * b:  [40]
a * b:  [40]
EvalMultMutableInPlace() with a and b with BGV...
a * b:  [5]
a * b:  [5]

Thanks. It looks like the problem is on the C++ side. We rarely use these functions (externally). I’ve created an issue to look into this bug further: Fix a bug with EvalMultMutable* · Issue #906 · openfheorg/openfhe-development · GitHub

1 Like