advanced-real-numbers.py 12 KB

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