Pyfhel icon indicating copy to clipboard operation
Pyfhel copied to clipboard

`HE.align_mod_n_scale(this, other)` not automatically called: `encrypted1 and encrypted2 parameter mismatch`

Open AlexMV12 opened this issue 3 years ago • 5 comments

Description In Demo_3_Float_CKKS.py there is written that:

To ease the life of the user, Pyfhel provides HE.align_mod_n_scale(this, other), which automatically does the rescaling and mod switching. All the 2-input overloaded operators (+, -, *, /) of PyCtxt automatically call this function. The respective HE.add, HE.sub, HE.multiply don't.

However, it looks like in some circumstances this does not happen.

Code To Reproduce Error Consider this code:

from Pyfhel import Pyfhel

HE = Pyfhel() 
HE.contextGen(scheme='ckks', n=16384, scale=2**30, qi=[60]+[30]*7+[60])  
HE.keyGen() 
HE.relinKeyGen()

ctxt1 = HE.encrypt(42.0)
print(ctxt1)
print(ctxt1.scale)
print(ctxt1.mod_level)

ptxt1 = HE.encode(1.0)
print(ptxt1)
print(ptxt1.scale)
print(ptxt1.mod_level)

ctxt2 = ctxt1 * ptxt1
HE.rescale_to_next(ctxt2)
print(ctxt2)
print(ctxt2.scale)
print(ctxt2.mod_level)

print(f"This works -> {ctxt2 + HE.encrypt(1.0)}")
print(f"This DOES NOT work -> {ctxt2 * HE.encrypt(1.0)}")

On Pyfhel==3.1.4 this results in an error:

<Pyfhel Ciphertext at 0x7f72b35c4480, scheme=ckks, size=2/2, scale_bits=30, mod_level=0>
1073741824.0
0
<Pyfhel Plaintext at 0x7f72b35c4f00, scheme=ckks, poly=?, is_ntt=Y, mod_level=0>
1073741824.0
0
<Pyfhel Ciphertext at 0x7f72b0dc5ac0, scheme=ckks, size=2/2, scale_bits=30, mod_level=1>
1073840136.0006409
1
This works -> <Pyfhel Ciphertext at 0x7f72b0db92c0, scheme=ckks, size=2/2, scale_bits=30, mod_level=1>

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [198], in <cell line: 18>()
     15 print(ctxt2.mod_level)
     17 print(f"This works -> {ctxt2 + HE.encrypt(1.0)}")
---> 18 print(f"This DOES NOT work -> {ctxt2 * HE.encrypt(1.0)}")

File Pyfhel/PyCtxt.pyx:437, in Pyfhel.PyCtxt.PyCtxt.__mul__()

File Pyfhel/Pyfhel.pyx:1016, in Pyfhel.Pyfhel.Pyfhel.multiply()

ValueError: encrypted1 and encrypted2 parameter mismatch

Morever, this happens also if the encryption in the problematic operation is replaced by an encoding:

print(f"This works -> {ctxt2 + HE.encode(1.0)}")
print(f"This DOES NOT work -> {ctxt2 * HE.encode(1.0)}")

In this case, the error becomes:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [199], in <cell line: 18>()
     15 print(ctxt2.mod_level)
     17 print(f"This works -> {ctxt2 + HE.encode(1.0)}")
---> 18 print(f"This DOES NOT work -> {ctxt2 * HE.encode(1.0)}")

File Pyfhel/PyCtxt.pyx:439, in Pyfhel.PyCtxt.PyCtxt.__mul__()

File Pyfhel/Pyfhel.pyx:1043, in Pyfhel.Pyfhel.Pyfhel.multiply_plain()

ValueError: encrypted_ntt and plain_ntt parameter mismatch

Expected behavior The automatic ciphertext maintenance should work also for multiplications, not only for additions; indeed, using manually the HE.align_mod_n_scale(this, other) before the multiplication results in a correct processing, without any problem.

Setup:

  • OS: Ubuntu 20.04 LTS
  • Python: [e.g. 3.8.10]
  • C compiler version: GCC 9.4.0
  • Pyfhel Version: 3.1.4

Thanks again for the great work! Sorry for all the issues, but Pyfhel is a valuable tool in my research and I hope to help you improve it. :)

AlexMV12 avatar Jun 15 '22 14:06 AlexMV12

Ok, it looks like simply the rows:

other_ = self.encode_operand(other)
self_, other_ = self._pyfhel.align_mod_n_scale(self, other_, 
                            copy_other=(other_ is other))

were missing in Pyfhel/PyCtxt.pyx.__mul__. Adding them and fixing the variables names seems to do the trick.

AlexMV12 avatar Jun 16 '22 09:06 AlexMV12

Very nice inspection! You don't need to align scales to multiply (the final scale will be just the multiplication of both scales), but I forgot you need to align mod_levels. I'm adding an extra parameter in align_mod_n_scale to do only that, and then adding it to PyCtxt.__mul__.

ibarrond avatar Jun 16 '22 09:06 ibarrond

Hurray!

ctxt1=<Pyfhel Ciphertext at 0x7fb9a9e8f840, scheme=ckks, size=2/2, scale_bits=30, mod_level=0>
ptxt1=<Pyfhel Plaintext at 0x7fb9a9e8f440, scheme=ckks, poly=?, is_ntt=Y, mod_level=0>
ctxt2=<Pyfhel Ciphertext at 0x7fb9a9e8f700, scheme=ckks, size=2/2, scale_bits=60, mod_level=1>
ctxt3=<Pyfhel Ciphertext at 0x7fb9a9e8fa40, scheme=ckks, size=2/2, scale_bits=30, mod_level=0>
This works -> <Pyfhel Ciphertext at 0x7fb9a9e8fd80, scheme=ckks, size=2/2, scale_bits=30, mod_level=1>
This DOES work -> <Pyfhel Ciphertext at 0x7fb9a9e8f240, scheme=ckks, size=3/3, scale_bits=90, mod_level=2>

ibarrond avatar Jun 16 '22 09:06 ibarrond

I confirm it works! Thanks!

AlexMV12 avatar Jun 16 '22 11:06 AlexMV12

Well, actually it looks like there is still something. :)

Consider this script:

from Pyfhel import Pyfhel

HE = Pyfhel()
HE.contextGen(scheme='ckks', n=16384, scale=2**30, qi=[60]+[30]*7+[60])
HE.keyGen()
HE.relinKeyGen()

ctxt1 = HE.encrypt(42.0) * HE.encrypt(42.0)
print(ctxt1)
print(ctxt1.scale)
print(ctxt1.mod_level)

ptxt1 = HE.encode(42.0)
print(ptxt1)
print(ptxt1.scale)
print(ptxt1.mod_level)

fixed_ctxt1, fixed_ptxt1 = HE.align_mod_n_scale(ctxt1, ptxt1, only_mod=True)

print(f"This works -> {fixed_ctxt1 * fixed_ptxt1}")
print(f"This DOES NOT work -> {ctxt1 * ptxt1}")

Note that in this case I did not any prior rescaling to ctxt1. However, if I manually do the align_mod_n_scale, it works. But it doesn't automagically work in the multiplication.

Changing PyCtxt.pyx.__mul__ to keep self does the trick:

self, other_ = self._pyfhel.align_mod_n_scale(self, other_, copy_other=(other_ is other), only_mod=True)

But I don't know why.

AlexMV12 avatar Jun 17 '22 13:06 AlexMV12

I added a regression test to keep track of this issue in test_ckks.py: https://github.com/ibarrond/Pyfhel/blob/f2c476d388f652e53b7c618a0590a80be456f939/Pyfhel/test/test_ckks.py#L31

As per v3.3.0, it has been fixed and should now work correctly.

ibarrond avatar Oct 04 '22 21:10 ibarrond