Explorar el Código

Merge pull request #27 from openfheorg/Oliveira_128

Oliveira 128
Rener Oliveira hace 1 año
padre
commit
6b251f12fa
Se han modificado 2 ficheros con 402 adiciones y 0 borrados
  1. 12 0
      src/bindings.cpp
  2. 390 0
      src/pke/examples/advanced-real-numbers-128.py

+ 12 - 0
src/bindings.cpp

@@ -99,6 +99,8 @@ void bind_crypto_context(py::module &m)
         .def("EvalAdd", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>) const>(&CryptoContextImpl<DCRTPoly>::EvalAdd), "Add two ciphertexts")
         .def("EvalAdd", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, double) const>(&CryptoContextImpl<DCRTPoly>::EvalAdd), "Add a ciphertext with a scalar")
         .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>) const>(&CryptoContextImpl<DCRTPoly>::EvalSub), "Subtract two ciphertexts")
+        .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, double) const>(&CryptoContextImpl<DCRTPoly>::EvalSub), "Subtract double from ciphertext")
+        .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(double, ConstCiphertext<DCRTPoly>) const>(&CryptoContextImpl<DCRTPoly>::EvalSub), "Subtract ciphertext from double")
         .def("EvalMult", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>) const>(&CryptoContextImpl<DCRTPoly>::EvalMult), "Multiply two ciphertexts")
         .def("EvalMult", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, double) const>(&CryptoContextImpl<DCRTPoly>::EvalMult), "Multiply a ciphertext with a scalar")
         .def("EvalLogistic", &CryptoContextImpl<DCRTPoly>::EvalLogistic,
@@ -181,6 +183,13 @@ void bind_crypto_context(py::module &m)
     m.def("ReleaseAllContexts", &CryptoContextFactory<DCRTPoly>::ReleaseAllContexts);
 }
 
+int get_native_int(){
+    #if NATIVEINT == 128 && !defined(__EMSCRIPTEN__)
+        return 128;
+    #else
+        return 64;    
+    #endif
+}
 void bind_enums_and_constants(py::module &m)
 {
     /* ---- PKE enums ---- */ 
@@ -237,6 +246,9 @@ void bind_enums_and_constants(py::module &m)
     /*TODO (Oliveira): If we expose Poly's and ParmType, this block will go somewhere else */
     using ParmType = typename DCRTPoly::Params;
     py::class_<ParmType, std::shared_ptr<ParmType>>(m, "ParmType");
+
+    //NATIVEINT function
+    m.def("get_native_int", &get_native_int);
 }
 
 void bind_keys(py::module &m)

+ 390 - 0
src/pke/examples/advanced-real-numbers-128.py

@@ -0,0 +1,390 @@
+from openfhe import *
+import time # to enable TIC-TOC timing measurements
+
+def automatic_rescale_demo(scal_tech):
+    if(scal_tech == ScalingTechnique.FLEXIBLEAUTO):
+        print("\n\n\n ===== FlexibleAutoDemo =============\n") 
+    else:
+         print("\n\n\n ===== FixedAutoDemo =============\n")
+
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(6)
+    parameters.SetScalingModSize(90)
+    parameters.SetScalingTechnique(scal_tech)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalMultKeyGen(keys.secretKey)
+
+    # Input
+    x = [1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # Computing f(x) = x^18 + x^9 + d
+    #
+    # In the following we compute f(x) with a computation
+    # that has a multiplicative depth of 5.
+    #
+    # The result is correct, even though there is no call to
+    # the Rescale() operation.
+
+    c2 = cc.EvalMult(c, c)                        # x^2
+    c4 = cc.EvalMult(c2, c2)                      # x^4
+    c8 = cc.EvalMult(c4, c4)                      # x^8
+    c16 = cc.EvalMult(c8, c8)                     # x^16
+    c9 = cc.EvalMult(c8, c)                       # x^9
+    c18 = cc.EvalMult(c16, c2)                    # x^18
+    c_res1 = cc.EvalAdd(cc.EvalAdd(c18, c9), 1.0)  # Final result 1
+    c_res2 = cc.EvalSub(cc.EvalAdd(c18,c9), 1.0)   # Final result 2
+    c_res3 = cc.EvalMult(cc.EvalAdd(c18,c9), 0.5)  # Final result 3
+
+    result1 = cc.Decrypt(c_res1,keys.secretKey)
+    result.SetLength(batch_size)
+    print("x^18 + x^9 + 1 = ", result1)
+    
+    result2 = cc.Decrypt(c_res2,keys.secretKey)
+    result.SetLength(batch_size)
+    print("x^18 + x^9 - 1 = ", result2)
+
+    result3 = cc.Decrypt(c_res3,keys.secretKey)
+    result.SetLength(batch_size)
+    print("0.5 * (x^18 + x^9) = ", result3)
+
+
+def manual_rescale_demo(scal_tech):
+    print("\n\n\n ===== FixedManualDemo =============\n")
+    
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+    
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalMultKeyGen(keys.secretKey)
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # Computing f(x) = x^18 + x^9 + 1
+    #
+    # Compare the following with the corresponding code
+    # for FIXEDAUTO. Here we need to track the depth of ciphertexts
+    # and call Rescale whenever needed. In this instance it's still
+    # not hard to do so, but this can be quite tedious in other
+    # complicated computations. (e.g. in bootstrapping)
+    #
+    #
+
+    # x^2
+    c2_depth2 = cc.EvalMult(c, c)
+    c2_depth1 = cc.Rescale(c2_depth2)
+    # x^4
+    c4_depth2 = cc.EvalMult(c2_depth1, c2_depth1)
+    c4_depth1 = cc.Rescale(c4_depth2)
+    # x^8
+    c8_depth2 = cc.EvalMult(c4_depth1, c4_depth1)
+    c8_depth1 = cc.Rescale(c8_depth2)
+    # x^16
+    c16_depth2 = cc.EvalMult(c8_depth1, c8_depth1)
+    c16_depth1 = cc.Rescale(c16_depth2)
+    # x^9
+    c9_depth2 = cc.EvalMult(c8_depth1, c)
+    # x^18
+    c18_depth2 = cc.EvalMult(c16_depth1, c2_depth1)
+    # Final result
+    c_res_depth2 = cc.EvalAdd(cc.EvalAdd(c18_depth2, c9_depth2), 1.0)
+    c_res_depth1 = cc.Rescale(c_res_depth2)
+
+    result = cc.Decrypt(c_res_depth1,keys.secretKey)
+    result.SetLength(batch_size)
+    print("x^18 + x^9 + 1 = ", result)
+
+def hybrid_key_switching_demo1():
+    
+    print("\n\n\n ===== hybrid_key_switching_demo1 ============= \n")
+    dnum = 2
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
+    parameters.SetNumLargeDigits(dnum)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    print(f"- Using HYBRID key switching with {dnum} digits\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c_rot1,-2)
+    time2digits = time.time() - t
+
+    result = cc.Decrypt(c_rot2,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"x rotate by -1 = {result}")
+    print(f" - 2 rotations with HYBRID (2 digits) took {time2digits*1000} ms")
+
+
+def hybrid_key_switching_demo2():
+    print("\n\n\n ===== hybrid_key_switching_demo2 =============\n")
+    dnum = 3
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
+    parameters.SetNumLargeDigits(dnum)
+
+    cc = GenCryptoContext(parameters)
+
+    # Compare the ring dimension in this demo to the one in the previous
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    print(f"- Using HYBRID key switching with {dnum} digits\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c_rot1,-2)
+    time3digits = time.time() - t
+    # The runtime here is smaller than the previous demo
+
+    result = cc.Decrypt(c_rot2,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"x rotate by -1 = {result}")
+    print(f" - 2 rotations with HYBRID (3 digits) took {time3digits*1000} ms")
+
+def fast_rotation_demo1():
+    print("\n\n\n ===== fast_rotation_demo1 =============\n")
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(1)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    N = cc.GetRingDimension()
+    print(f"CKKS scheme is using ring dimension {N}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
+
+    # Input
+    x = [0, 0, 0, 0, 0, 0, 0, 1]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # First, we perform 7 regular (non-hoisted) rotations
+    # and measure the runtime
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c,2)
+    c_rot3 = cc.EvalRotate(c,3)
+    c_rot4 = cc.EvalRotate(c,4)
+    c_rot5 = cc.EvalRotate(c,5)
+    c_rot6 = cc.EvalRotate(c,6)
+    c_rot7 = cc.EvalRotate(c,7)
+    time_no_hoisting = time.time() - t
+
+    c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    # M is the cyclotomic order and we need it to call EvalFastRotation
+    M = 2*N
+
+    # Then, we perform 7 rotations with hoisting.
+    t = time.time()
+    c_precomp = cc.EvalFastRotationPrecompute(c)
+    c_rot1 = cc.EvalFastRotation(c, 1, M, c_precomp)
+    c_rot2 = cc.EvalFastRotation(c, 2, M, c_precomp)
+    c_rot3 = cc.EvalFastRotation(c, 3, M, c_precomp)
+    c_rot4 = cc.EvalFastRotation(c, 4, M, c_precomp)
+    c_rot5 = cc.EvalFastRotation(c, 5, M, c_precomp)
+    c_rot6 = cc.EvalFastRotation(c, 6, M, c_precomp)
+    c_rot7 = cc.EvalFastRotation(c, 7, M, c_precomp)
+    time_hoisting = time.time() - t
+    # The time with hoisting should be faster than without hoisting.
+
+    c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+    
+    result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result without hoisting: {result}")
+    print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
+
+    
+    result = cc.Decrypt(c_res_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result with hoisting: {result}")
+    print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
+
+
+
+
+def fast_rotation_demo2():
+    print("\n\n\n ===== fast_rotation_demo2 =============\n")
+
+    batch_size = 8
+
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(1)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
+    parameters.SetKeySwitchTechnique(KeySwitchTechnique.BV)
+
+    digit_size = 10
+    first_mod_size = 100
+    parameters.SetFirstModSize(first_mod_size)
+    parameters.SetDigitSize(digit_size)
+
+    cc = GenCryptoContext(parameters)
+
+    N = cc.GetRingDimension()
+    print(f"CKKS scheme is using ring dimension {N}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
+
+    # Input
+    x = [0, 0, 0, 0, 0, 0, 0, 1]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # First, we perform 7 regular (non-hoisted) rotations
+    # and measure the runtime
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c,2)
+    c_rot3 = cc.EvalRotate(c,3)
+    c_rot4 = cc.EvalRotate(c,4)
+    c_rot5 = cc.EvalRotate(c,5)
+    c_rot6 = cc.EvalRotate(c,6)
+    c_rot7 = cc.EvalRotate(c,7)
+    time_no_hoisting = time.time() - t
+
+    c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    # M is the cyclotomic order and we need it to call EvalFastRotation
+    M = 2*N
+
+    # Then, we perform 7 rotations with hoisting.
+    t = time.time()
+    c_precomp = cc.EvalFastRotationPrecompute(c)
+    c_rot1 = cc.EvalFastRotation(c, 1, M, c_precomp)
+    c_rot2 = cc.EvalFastRotation(c, 2, M, c_precomp)
+    c_rot3 = cc.EvalFastRotation(c, 3, M, c_precomp)
+    c_rot4 = cc.EvalFastRotation(c, 4, M, c_precomp)
+    c_rot5 = cc.EvalFastRotation(c, 5, M, c_precomp)
+    c_rot6 = cc.EvalFastRotation(c, 6, M, c_precomp)
+    c_rot7 = cc.EvalFastRotation(c, 7, M, c_precomp)
+    time_hoisting = time.time() - t
+    # The time with hoisting should be faster than without hoisting.
+    # Also, the benefits from hoisting should be more pronounced in this
+    # case because we're using BV. Of course, we also observe less
+    # accurate results than when using HYBRID, because of using
+    # digitSize = 10 (Users can decrease digitSize to see the accuracy
+    # increase, and performance decrease).
+
+    c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result without hoisting: {result}")
+    print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
+
+    result = cc.Decrypt(c_res_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result with hoisting: {result}")
+    print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
+
+
+def main():
+    if get_native_int() == 128:
+        automatic_rescale_demo(ScalingTechnique.FIXEDAUTO)
+        # Note that FLEXIBLEAUTO is not supported for 128-bit CKKS
+        manual_rescale_demo(ScalingTechnique.FIXEDMANUAL)
+        hybrid_key_switching_demo1()
+        hybrid_key_switching_demo2()
+        fast_rotation_demo1()
+        fast_rotation_demo2()
+    else:
+        print("This demo only runs for 128-bit CKKS.\n")
+
+if __name__ == "__main__":
+    main()
+