openfheorg / openfhe-development

This is the development repository for the OpenFHE library. The current (stable) version is v1.2.1 (released on September 10, 2024).
BSD 2-Clause "Simplified" License
715 stars 185 forks source link

Setting a Larger Value in SetScalingModSize() Leads to Incorrect Results and Crash #364

Closed ailanxier closed 1 year ago

ailanxier commented 1 year ago

In simple-real-numbers example,using scaleModSize=59 (i.e., the default value of the CKKS scheme) leads to incorrect multiplication results, as shown below.

// test code
uint32_t scaleModSize = 59;

// result
CKKS scheme is using ring dimension 16384

Input x1: (0.25, 0.5, 0.75, 1, 2, 3, 4, 5,  ... ); Estimated precision: 59 bits

Input x2: (5, 4, 3, 2, 1, 0.75, 0.5, 0.25,  ... ); Estimated precision: 59 bits

Results of homomorphic computations:
x1 = (0.25, 0.5, 0.75, 1, 2, 3, 4, 5,  ... ); Estimated precision: 49 bits
Estimated precision in bits: 49
x1 + x2 = (5.25, 4.5, 3.75, 3, 3, 3.75, 4.5, 5.25,  ... ); Estimated precision: 51 bits
Estimated precision in bits: 51
x1 - x2 = (-4.75, -3.5, -2.25, -1, 1, 2.25, 3.5, 4.75,  ... ); Estimated precision: 48 bits

4 * x1 = (-1.7292238, 2.7373427, 1.1754109, 1.958881, -2.2093088, 2.6536936, -1.2368784, -1.3499172,  ... ); Estimated precision: 47 bits

x1 * x2 = (-0.75, 3.7024828e-12, 0.25, 3.691325e-12, 3.6974313e-12, 0.25, 3.688147e-12, -0.75,  ... ); Estimated precision: 48 bits

In rotations, very small outputs (~10^-10 here) correspond to 0's:
x1 rotate by 1 = (0.5, 0.75, 1, 2, 3, 4, 5, 0.25,  ... ); Estimated precision: 49 bits

x1 rotate by -2 = (4, 5, 0.25, 0.5, 0.75, 1, 2, 3,  ... ); Estimated precision: 49 bits

The values of 4 * x1 and x1 * x2 are obviously incorrect. However, when changing multDepth to 2 (the original value in the example is 1), the values of 4 * x1 and x1 * x2 are correct. There is no other prompt or error message during this process. If scaleModSize is used incorrectly, a corresponding prompt should be given.

At the same time, I met a similar situation as #339 in the BGV scheme(simple-integers-bgvrns example). My code and result are as follows.

// test code
    CCParams<CryptoContextBGVRNS> parameters;
    parameters.SetMultiplicativeDepth(2);
    parameters.SetPlaintextModulus(65537);
    parameters.SetFirstModSize(2);
    parameters.SetScalingModSize(1);

    CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);

// result
terminate called after throwing an instance of 'lbcrypto::not_available_error'
  what():  /root/openfhe-development/src/core/lib/math/hal/intnat/mubintvecnat.cpp:57 Modulus size 64 is too large. NativeVectorT supports only modulus size <=  60 bits
[1]    112173 IOT instruction (core dumped)  ./bin/simple-integers-bgvrns

However, when I change scaleModSize to a larger value, such as 50, an error occurs.

// test code
    CCParams<CryptoContextBGVRNS> parameters;
    parameters.SetMultiplicativeDepth(2);
    parameters.SetPlaintextModulus(65537);
    parameters.SetFirstModSize(60);        // The same crash occurs without this line.
    parameters.SetScalingModSize(50);

    CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);   // simple-integers-bgvrns.cpp:48

// Not use ASAN
[1]    110266 floating point exception (core dumped)  ./bin/simple-integers-bgvrns

// ASAN report
 AddressSanitizer:DEADLYSIGNAL
=================================================================
==109958==ERROR: AddressSanitizer: FPE on unknown address 0x55578b5e9397 (pc 0x55578b5e9397 bp 0xffffffffffffff90 sp 0x7ffc1f104668 T0)
    #0 0x55578b5e9397 in __udivti3 (/root/openfhe-development/build_static/bin/simple-integers-bgvrns+0xc26397)
    #1 0x55578ad03e4c in intnat::NativeIntegerT<unsigned long> intnat::NativeIntegerT<unsigned long>::ComputeMu<unsigned long>(std::enable_if<!std::is_same<unsigned long, unsigned __int128>::value, bool>::type) const /root/openfhe-development/src/core/include/math/hal/intnat/ubintnat.h:738
    #2 0x55578ad03e4c in intnat::ChineseRemainderTransformFTTNat<intnat::NativeVectorT<intnat::NativeIntegerT<unsigned long> > >::PreCompute(intnat::NativeIntegerT<unsigned long> const&, unsigned int, intnat::NativeIntegerT<unsigned long> const&) /root/openfhe-development/src/core/include/math/hal/intnat/transformnat-impl.h:695
    #3 0x55578acbf553 in intnat::ChineseRemainderTransformFTTNat<intnat::NativeVectorT<intnat::NativeIntegerT<unsigned long> > >::PreCompute(std::vector<intnat::NativeIntegerT<unsigned long>, std::allocator<intnat::NativeIntegerT<unsigned long> > >&, unsigned int, std::vector<intnat::NativeIntegerT<unsigned long>, std::allocator<intnat::NativeIntegerT<unsigned long> > >&) /root/openfhe-development/src/core/include/math/hal/intnat/transformnat-impl.h:754
    #4 0x55578ad9a399 in lbcrypto::ParameterGenerationBGVRNS::ParamsGenBGVRNS(std::shared_ptr<lbcrypto::CryptoParametersBase<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned int> > > > >, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int) const /root/openfhe-development/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp:472
    #5 0x55578ab02303 in lbcrypto::SchemeBase<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned int> > > >::ParamsGenBGVRNS(std::shared_ptr<lbcrypto::CryptoParametersBase<lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned int> > > > >, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int) const /root/openfhe-development/src/pke/include/schemebase/base-scheme.h:174
    #6 0x55578ab65be9 in lbcrypto::CryptoContextBGVRNS::ContextType lbcrypto::genCryptoContextBGVRNSInternal<lbcrypto::CryptoContextBGVRNS, lbcrypto::DCRTPolyImpl<bigintdyn::mubintvec<bigintdyn::ubint<unsigned int> > > >(lbcrypto::CCParams<lbcrypto::CryptoContextBGVRNS> const&) /root/openfhe-development/src/pke/include/scheme/bgvrns/gen-cryptocontext-bgvrns-internal.h:93
    #7 0x55578aa770c7 in lbcrypto::CryptoContextBGVRNS::genCryptoContext(lbcrypto::CCParams<lbcrypto::CryptoContextBGVRNS> const&) /root/openfhe-development/src/pke/include/scheme/bgvrns/cryptocontext-bgvrns.h:61
    #8 0x55578aa770c7 in lbcrypto::CryptoContextBGVRNS::ContextType lbcrypto::GenCryptoContext<lbcrypto::CryptoContextBGVRNS>(lbcrypto::CCParams<lbcrypto::CryptoContextBGVRNS> const&) /root/openfhe-development/src/pke/include/gen-cryptocontext.h:90
    #9 0x55578aa770c7 in main /root/openfhe-development/src/pke/examples/simple-integers-bgvrns.cpp:48
    #10 0x7f1ba9ac3d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)
    #11 0x7f1ba9ac3e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f)
    #12 0x55578aab4e34 in _start (/root/openfhe-development/build_static/bin/simple-integers-bgvrns+0xf1e34)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: FPE (/root/openfhe-development/build_static/bin/simple-integers-bgvrns+0xc26397) in __udivti3
==109958==ABORTING

And I am not sure if it is necessary to set scaleModSize or call Rescale() in the BGV scheme. When I set parameters.SetScalingTechnique(FIXEDMANUAL); in simple-integers-bgvrns example, I can get correct results without manually calling Rescale(), which confuses me. Thanks in advance for confirming and answering the above questions.

yspolyakov commented 1 year ago

The first behavior is expected. Setting the scaling mod size to 59 bits leaves no room for supporting larger values (higher than 1 or higher) in CKKS. Increasing the multiplicative depth one of the ways to fix it. Only the application developer knows the magnitude of encrypted messages. So OpenFHE cannot catch this as the underlying messages are encrypted.

For the second issue (a small scaling mod size), there is an issue already: https://github.com/openfheorg/openfhe-development/issues/339