advanced-real-numbers-128.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. from openfhe import *
  2. import time # to enable TIC-TOC timing measurements
  3. def automatic_rescale_demo(scal_tech):
  4. if(scal_tech == ScalingTechnique.FLEXIBLEAUTO):
  5. print("\n\n\n ===== FlexibleAutoDemo =============\n")
  6. else:
  7. print("\n\n\n ===== FixedAutoDemo =============\n")
  8. batch_size = 8
  9. parameters = CCParamsCKKSRNS()
  10. parameters.SetMultiplicativeDepth(6)
  11. parameters.SetScalingModSize(90)
  12. parameters.SetScalingTechnique(scal_tech)
  13. parameters.SetBatchSize(batch_size)
  14. cc = GenCryptoContext(parameters)
  15. print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
  16. cc.Enable(PKESchemeFeature.PKE)
  17. cc.Enable(PKESchemeFeature.KEYSWITCH)
  18. cc.Enable(PKESchemeFeature.LEVELEDSHE)
  19. keys = cc.KeyGen()
  20. cc.EvalMultKeyGen(keys.secretKey)
  21. # Input
  22. x = [1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07]
  23. ptxt = cc.MakeCKKSPackedPlaintext(x)
  24. print(f"Input x: {ptxt}")
  25. c = cc.Encrypt(keys.publicKey,ptxt)
  26. # Computing f(x) = x^18 + x^9 + d
  27. #
  28. # In the following we compute f(x) with a computation
  29. # that has a multiplicative depth of 5.
  30. #
  31. # The result is correct, even though there is no call to
  32. # the Rescale() operation.
  33. c2 = cc.EvalMult(c, c) # x^2
  34. c4 = cc.EvalMult(c2, c2) # x^4
  35. c8 = cc.EvalMult(c4, c4) # x^8
  36. c16 = cc.EvalMult(c8, c8) # x^16
  37. c9 = cc.EvalMult(c8, c) # x^9
  38. c18 = cc.EvalMult(c16, c2) # x^18
  39. c_res1 = cc.EvalAdd(cc.EvalAdd(c18, c9), 1.0) # Final result 1
  40. c_res2 = cc.EvalSub(cc.EvalAdd(c18,c9), 1.0) # Final result 2
  41. c_res3 = cc.EvalMult(cc.EvalAdd(c18,c9), 0.5) # Final result 3
  42. result1 = cc.Decrypt(c_res1,keys.secretKey)
  43. result.SetLength(batch_size)
  44. print("x^18 + x^9 + 1 = ", result1)
  45. result2 = cc.Decrypt(c_res2,keys.secretKey)
  46. result.SetLength(batch_size)
  47. print("x^18 + x^9 - 1 = ", result2)
  48. result3 = cc.Decrypt(c_res3,keys.secretKey)
  49. result.SetLength(batch_size)
  50. print("0.5 * (x^18 + x^9) = ", result3)
  51. def manual_rescale_demo(scal_tech):
  52. print("\n\n\n ===== FixedManualDemo =============\n")
  53. batch_size = 8
  54. parameters = CCParamsCKKSRNS()
  55. parameters.SetMultiplicativeDepth(5)
  56. parameters.SetScalingModSize(90)
  57. parameters.SetBatchSize(batch_size)
  58. cc = GenCryptoContext(parameters)
  59. print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
  60. cc.Enable(PKESchemeFeature.PKE)
  61. cc.Enable(PKESchemeFeature.KEYSWITCH)
  62. cc.Enable(PKESchemeFeature.LEVELEDSHE)
  63. keys = cc.KeyGen()
  64. cc.EvalMultKeyGen(keys.secretKey)
  65. # Input
  66. x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
  67. ptxt = cc.MakeCKKSPackedPlaintext(x)
  68. print(f"Input x: {ptxt}")
  69. c = cc.Encrypt(keys.publicKey,ptxt)
  70. # Computing f(x) = x^18 + x^9 + 1
  71. #
  72. # Compare the following with the corresponding code
  73. # for FIXEDAUTO. Here we need to track the depth of ciphertexts
  74. # and call Rescale whenever needed. In this instance it's still
  75. # not hard to do so, but this can be quite tedious in other
  76. # complicated computations. (e.g. in bootstrapping)
  77. #
  78. #
  79. # x^2
  80. c2_depth2 = cc.EvalMult(c, c)
  81. c2_depth1 = cc.Rescale(c2_depth2)
  82. # x^4
  83. c4_depth2 = cc.EvalMult(c2_depth1, c2_depth1)
  84. c4_depth1 = cc.Rescale(c4_depth2)
  85. # x^8
  86. c8_depth2 = cc.EvalMult(c4_depth1, c4_depth1)
  87. c8_depth1 = cc.Rescale(c8_depth2)
  88. # x^16
  89. c16_depth2 = cc.EvalMult(c8_depth1, c8_depth1)
  90. c16_depth1 = cc.Rescale(c16_depth2)
  91. # x^9
  92. c9_depth2 = cc.EvalMult(c8_depth1, c)
  93. # x^18
  94. c18_depth2 = cc.EvalMult(c16_depth1, c2_depth1)
  95. # Final result
  96. c_res_depth2 = cc.EvalAdd(cc.EvalAdd(c18_depth2, c9_depth2), 1.0)
  97. c_res_depth1 = cc.Rescale(c_res_depth2)
  98. result = cc.Decrypt(c_res_depth1,keys.secretKey)
  99. result.SetLength(batch_size)
  100. print("x^18 + x^9 + 1 = ", result)
  101. def hybrid_key_switching_demo1():
  102. print("\n\n\n ===== hybrid_key_switching_demo1 ============= \n")
  103. dnum = 2
  104. batch_size = 8
  105. parameters = CCParamsCKKSRNS()
  106. parameters.SetMultiplicativeDepth(5)
  107. parameters.SetScalingModSize(90)
  108. parameters.SetBatchSize(batch_size)
  109. parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
  110. parameters.SetNumLargeDigits(dnum)
  111. cc = GenCryptoContext(parameters)
  112. print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
  113. print(f"- Using HYBRID key switching with {dnum} digits\n")
  114. cc.Enable(PKESchemeFeature.PKE)
  115. cc.Enable(PKESchemeFeature.KEYSWITCH)
  116. cc.Enable(PKESchemeFeature.LEVELEDSHE)
  117. keys = cc.KeyGen()
  118. cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
  119. # Input
  120. x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
  121. ptxt = cc.MakeCKKSPackedPlaintext(x)
  122. print(f"Input x: {ptxt}")
  123. c = cc.Encrypt(keys.publicKey,ptxt)
  124. t = time.time()
  125. c_rot1 = cc.EvalRotate(c,1)
  126. c_rot2 = cc.EvalRotate(c_rot1,-2)
  127. time2digits = time.time() - t
  128. result = cc.Decrypt(c_rot2,keys.secretKey)
  129. result.SetLength(batch_size)
  130. print(f"x rotate by -1 = {result}")
  131. print(f" - 2 rotations with HYBRID (2 digits) took {time2digits*1000} ms")
  132. def hybrid_key_switching_demo2():
  133. print("\n\n\n ===== hybrid_key_switching_demo2 =============\n")
  134. dnum = 3
  135. batch_size = 8
  136. parameters = CCParamsCKKSRNS()
  137. parameters.SetMultiplicativeDepth(5)
  138. parameters.SetScalingModSize(90)
  139. parameters.SetBatchSize(batch_size)
  140. parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
  141. parameters.SetNumLargeDigits(dnum)
  142. cc = GenCryptoContext(parameters)
  143. # Compare the ring dimension in this demo to the one in the previous
  144. print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
  145. print(f"- Using HYBRID key switching with {dnum} digits\n")
  146. cc.Enable(PKESchemeFeature.PKE)
  147. cc.Enable(PKESchemeFeature.KEYSWITCH)
  148. cc.Enable(PKESchemeFeature.LEVELEDSHE)
  149. keys = cc.KeyGen()
  150. cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
  151. # Input
  152. x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
  153. ptxt = cc.MakeCKKSPackedPlaintext(x)
  154. print(f"Input x: {ptxt}")
  155. c = cc.Encrypt(keys.publicKey,ptxt)
  156. t = time.time()
  157. c_rot1 = cc.EvalRotate(c,1)
  158. c_rot2 = cc.EvalRotate(c_rot1,-2)
  159. time3digits = time.time() - t
  160. # The runtime here is smaller than the previous demo
  161. result = cc.Decrypt(c_rot2,keys.secretKey)
  162. result.SetLength(batch_size)
  163. print(f"x rotate by -1 = {result}")
  164. print(f" - 2 rotations with HYBRID (3 digits) took {time3digits*1000} ms")
  165. def fast_rotation_demo1():
  166. print("\n\n\n ===== fast_rotation_demo1 =============\n")
  167. batch_size = 8
  168. parameters = CCParamsCKKSRNS()
  169. parameters.SetMultiplicativeDepth(1)
  170. parameters.SetScalingModSize(90)
  171. parameters.SetBatchSize(batch_size)
  172. cc = GenCryptoContext(parameters)
  173. N = cc.GetRingDimension()
  174. print(f"CKKS scheme is using ring dimension {N}\n")
  175. cc.Enable(PKESchemeFeature.PKE)
  176. cc.Enable(PKESchemeFeature.KEYSWITCH)
  177. cc.Enable(PKESchemeFeature.LEVELEDSHE)
  178. keys = cc.KeyGen()
  179. cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
  180. # Input
  181. x = [0, 0, 0, 0, 0, 0, 0, 1]
  182. ptxt = cc.MakeCKKSPackedPlaintext(x)
  183. print(f"Input x: {ptxt}")
  184. c = cc.Encrypt(keys.publicKey,ptxt)
  185. # First, we perform 7 regular (non-hoisted) rotations
  186. # and measure the runtime
  187. t = time.time()
  188. c_rot1 = cc.EvalRotate(c,1)
  189. c_rot2 = cc.EvalRotate(c,2)
  190. c_rot3 = cc.EvalRotate(c,3)
  191. c_rot4 = cc.EvalRotate(c,4)
  192. c_rot5 = cc.EvalRotate(c,5)
  193. c_rot6 = cc.EvalRotate(c,6)
  194. c_rot7 = cc.EvalRotate(c,7)
  195. time_no_hoisting = time.time() - t
  196. c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
  197. # M is the cyclotomic order and we need it to call EvalFastRotation
  198. M = 2*N
  199. # Then, we perform 7 rotations with hoisting.
  200. t = time.time()
  201. c_precomp = cc.EvalFastRotationPrecompute(c)
  202. c_rot1 = cc.EvalFastRotation(c, 1, M, c_precomp)
  203. c_rot2 = cc.EvalFastRotation(c, 2, M, c_precomp)
  204. c_rot3 = cc.EvalFastRotation(c, 3, M, c_precomp)
  205. c_rot4 = cc.EvalFastRotation(c, 4, M, c_precomp)
  206. c_rot5 = cc.EvalFastRotation(c, 5, M, c_precomp)
  207. c_rot6 = cc.EvalFastRotation(c, 6, M, c_precomp)
  208. c_rot7 = cc.EvalFastRotation(c, 7, M, c_precomp)
  209. time_hoisting = time.time() - t
  210. # The time with hoisting should be faster than without hoisting.
  211. c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
  212. result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
  213. result.SetLength(batch_size)
  214. print(f"Result without hoisting: {result}")
  215. print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
  216. result = cc.Decrypt(c_res_hoist,keys.secretKey)
  217. result.SetLength(batch_size)
  218. print(f"Result with hoisting: {result}")
  219. print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
  220. def fast_rotation_demo2():
  221. print("\n\n\n ===== fast_rotation_demo2 =============\n")
  222. batch_size = 8
  223. parameters = CCParamsCKKSRNS()
  224. parameters.SetMultiplicativeDepth(1)
  225. parameters.SetScalingModSize(90)
  226. parameters.SetBatchSize(batch_size)
  227. parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
  228. parameters.SetKeySwitchTechnique(KeySwitchTechnique.BV)
  229. digit_size = 10
  230. first_mod_size = 100
  231. parameters.SetFirstModSize(first_mod_size)
  232. parameters.SetDigitSize(digit_size)
  233. cc = GenCryptoContext(parameters)
  234. N = cc.GetRingDimension()
  235. print(f"CKKS scheme is using ring dimension {N}\n")
  236. cc.Enable(PKESchemeFeature.PKE)
  237. cc.Enable(PKESchemeFeature.KEYSWITCH)
  238. cc.Enable(PKESchemeFeature.LEVELEDSHE)
  239. keys = cc.KeyGen()
  240. cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
  241. # Input
  242. x = [0, 0, 0, 0, 0, 0, 0, 1]
  243. ptxt = cc.MakeCKKSPackedPlaintext(x)
  244. print(f"Input x: {ptxt}")
  245. c = cc.Encrypt(keys.publicKey,ptxt)
  246. # First, we perform 7 regular (non-hoisted) rotations
  247. # and measure the runtime
  248. t = time.time()
  249. c_rot1 = cc.EvalRotate(c,1)
  250. c_rot2 = cc.EvalRotate(c,2)
  251. c_rot3 = cc.EvalRotate(c,3)
  252. c_rot4 = cc.EvalRotate(c,4)
  253. c_rot5 = cc.EvalRotate(c,5)
  254. c_rot6 = cc.EvalRotate(c,6)
  255. c_rot7 = cc.EvalRotate(c,7)
  256. time_no_hoisting = time.time() - t
  257. c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
  258. # M is the cyclotomic order and we need it to call EvalFastRotation
  259. M = 2*N
  260. # Then, we perform 7 rotations with hoisting.
  261. t = time.time()
  262. c_precomp = cc.EvalFastRotationPrecompute(c)
  263. c_rot1 = cc.EvalFastRotation(c, 1, M, c_precomp)
  264. c_rot2 = cc.EvalFastRotation(c, 2, M, c_precomp)
  265. c_rot3 = cc.EvalFastRotation(c, 3, M, c_precomp)
  266. c_rot4 = cc.EvalFastRotation(c, 4, M, c_precomp)
  267. c_rot5 = cc.EvalFastRotation(c, 5, M, c_precomp)
  268. c_rot6 = cc.EvalFastRotation(c, 6, M, c_precomp)
  269. c_rot7 = cc.EvalFastRotation(c, 7, M, c_precomp)
  270. time_hoisting = time.time() - t
  271. # The time with hoisting should be faster than without hoisting.
  272. # Also, the benefits from hoisting should be more pronounced in this
  273. # case because we're using BV. Of course, we also observe less
  274. # accurate results than when using HYBRID, because of using
  275. # digitSize = 10 (Users can decrease digitSize to see the accuracy
  276. # increase, and performance decrease).
  277. c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
  278. result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
  279. result.SetLength(batch_size)
  280. print(f"Result without hoisting: {result}")
  281. print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
  282. result = cc.Decrypt(c_res_hoist,keys.secretKey)
  283. result.SetLength(batch_size)
  284. print(f"Result with hoisting: {result}")
  285. print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
  286. def main():
  287. if get_native_int() == 128:
  288. automatic_rescale_demo(ScalingTechnique.FIXEDAUTO)
  289. # Note that FLEXIBLEAUTO is not supported for 128-bit CKKS
  290. manual_rescale_demo(ScalingTechnique.FIXEDMANUAL)
  291. hybrid_key_switching_demo1()
  292. hybrid_key_switching_demo2()
  293. fast_rotation_demo1()
  294. fast_rotation_demo2()
  295. else:
  296. print("This demo only runs for 128-bit CKKS.\nIf you want to test it please reinstall the OpenFHE C++ with the flag -DNATIVE_INT=128, then reinstall OpenFHE-Python.")
  297. if __name__ == "__main__":
  298. main()