ソースを参照

Merge pull request #20 from openfheorg/Oliveira_bootstrapping

CKKS Bootstrapping
Rener Oliveira 1 年間 前
コミット
4b99e83eed

+ 2 - 1
.gitignore

@@ -1,2 +1,3 @@
 build/
-.vscode/
+.vscode/
+.idea

+ 0 - 16
CMakeLists.txt

@@ -1,21 +1,5 @@
 cmake_minimum_required (VERSION 3.5.1)
 
-### To use gcc/g++ on a Macintosh, you must set the Compilers
-### here, not inside the project
-##if(APPLE)
-##       set(CMAKE_C_COMPILER "/usr/local/bin/gcc-7")
-##       set(CMAKE_CXX_COMPILER "/usr/local/bin/g++-7")
-##endif()
-### TODO: for now, we use CLang for Mac
-###
-### In order to create OpenFHE's static libraries you should enable
-### the BUILD_STATIC option. For that, you run "cmake .. -DBUILD_STATIC=ON".
-### After having your link completed you will find static libs
-### with the suffix "_static" in ./build/libs/.
-### Examples: OPENFHEpke_static.a, OPENFHEcore_static.a, etc.
-### After you run "make install" in your build directory, you can build your custom application.
-### If you need your application to be linked statically, then run "cmake .. -DBUILD_STATIC=ON"
-
 project(openfhe)
 set(CMAKE_CXX_STANDARD 17)
 option( BUILD_STATIC "Set to ON to include static versions of the library" OFF)

+ 43 - 3
README.md

@@ -1,7 +1,18 @@
 # [Work in Progress] Official Python wrapper for OpenFHE
 
+## Table of Contents
+
+- [Building](#building)
+  - [Prerequisites](#requirements)
+  - [Linux Install](#linux)
+    - [Installing the .so: Conda](#conda)
+    - [Installing the .so: System](#system-install)
+- [Running Examples](#examples)
+
 ## Building
 
+### Requirements
+
 Before building, make sure you have the following dependencies installed:
 
 - [CMake](https://cmake.org/)
@@ -16,11 +27,40 @@ With all the dependencies set up, clone the repository, open a terminal in the r
 ```bash
 mkdir build
 cd build
-cmake ..
+cmake ..  // Alternatively, cmake .. -DOpenFHE_DIR=/path/to/installed/openfhe
 make
-make install
 ```
-Obs.: If the last command fails, try running it with sudo.
+
+At this point the `.so` file has been built. Your exact installation process will depend on your virtual environment.
+
+#### Conda
+
+```bash
+conda create -n ${ENV_NAME} python=3.{X} anaconda
+```
+
+where `${ENV_NAME}` should be replaced with the name of your environment, and `{X}` should be replaced with your desired python version. For example you might have `
+conda create -n openfhe_python python=3.9 anaconda`
+
+then run 
+
+```
+mkdir lib
+mv *.so lib
+conda develop lib
+```
+
+which creates a lib folder, moves the built `.so` file into that lib folder, and tells conda where to look for external libraries.
+
+**Note** You may wish to copy the `.so` file to any projects of your own, or add it to your system path to source from.
+
+#### System install
+
+```
+make install  // You may have to run sudo make install
+```
+
+## Examples
 
 After that, you can run the examples in the src/pke/examples folder:
 

+ 1 - 0
include/bindings.h

@@ -11,4 +11,5 @@ void bind_encodings(pybind11::module &m);
 void bind_ciphertext(pybind11::module &m);
 void bind_decryption(pybind11::module &m);
 void bind_serialization(pybind11::module &m);
+void bind_schemes(pybind11::module &m);
 #endif // OPENFHE_BINDINGS_H

+ 46 - 3
src/bindings.cpp

@@ -43,6 +43,11 @@ void bind_parameters(py::module &m)
         .def("SetKeySwitchTechnique", &CCParams<CryptoContextCKKSRNS>::SetKeySwitchTechnique)
         .def("SetFirstModSize", &CCParams<CryptoContextCKKSRNS>::SetFirstModSize)
         .def("SetDigitSize", &CCParams<CryptoContextCKKSRNS>::SetDigitSize)
+        .def("SetSecretKeyDist", &CCParams<CryptoContextCKKSRNS>::SetSecretKeyDist)
+        .def("SetSecurityLevel", &CCParams<CryptoContextCKKSRNS>::SetSecurityLevel)
+        .def("SetRingDim", &CCParams<CryptoContextCKKSRNS>::SetRingDim)
+        .def("SetScalingModSize", &CCParams<CryptoContextCKKSRNS>::SetScalingModSize)
+
         // getters
         .def("GetPlaintextModulus", &CCParams<CryptoContextCKKSRNS>::GetPlaintextModulus)
         .def("GetMultiplicativeDepth", &CCParams<CryptoContextCKKSRNS>::GetMultiplicativeDepth)
@@ -84,6 +89,18 @@ void bind_crypto_context(py::module &m)
         .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("Rescale", &CryptoContextImpl<DCRTPoly>::Rescale, "Rescale a ciphertext")
+        .def("EvalBootstrapSetup", &CryptoContextImpl<DCRTPoly>::EvalBootstrapSetup,
+            py::arg("levelBudget") = std::vector<uint32_t>({5,4}),
+            py::arg("dim1") = std::vector<uint32_t>({0,0}),
+            py::arg("slots") = 0,
+            py::arg("correctionFactor") = 0            )
+        .def("EvalBootstrapKeyGen", &CryptoContextImpl<DCRTPoly>::EvalBootstrapKeyGen,
+            py::arg("privateKey"),
+            py::arg("slots"))
+        .def("EvalBootstrap", &CryptoContextImpl<DCRTPoly>::EvalBootstrap,
+            py::arg("ciphertext"),
+            py::arg("numIterations") = 1,
+            py::arg("precision") = 0)
         .def_static(
             "ClearEvalMultKeys", []()
             { CryptoContextImpl<DCRTPoly>::ClearEvalMultKeys(); },
@@ -139,6 +156,7 @@ void bind_crypto_context(py::module &m)
 
 void bind_enums_and_constants(py::module &m)
 {
+    /* ---- PKE enums ---- */ 
     // Scheme Types
     py::enum_<SCHEME>(m, "SCHEME")
         .value("INVALID_SCHEME", SCHEME::INVALID_SCHEME)
@@ -173,8 +191,23 @@ void bind_enums_and_constants(py::module &m)
         .value("INVALID_KS_TECH", KeySwitchTechnique::INVALID_KS_TECH)
         .value("BV", KeySwitchTechnique::BV)
         .value("HYBRID", KeySwitchTechnique::HYBRID);
+    // Secret Key Dist
+    py::enum_<SecretKeyDist>(m, "SecretKeyDist")
+        .value("GAUSSIAN", SecretKeyDist::GAUSSIAN)
+        .value("UNIFORM_TERNARY", SecretKeyDist::UNIFORM_TERNARY)
+        .value("SPARsE_TERNARY", SecretKeyDist::SPARSE_TERNARY);
+
+    /* ---- CORE enums ---- */ 
+    // Security Level
+    py::enum_<SecurityLevel>(m,"SecurityLevel")
+        .value("HEStd_128_classic", SecurityLevel::HEStd_128_classic)
+        .value("HEStd_192_classic", SecurityLevel::HEStd_192_classic)
+        .value("HEStd_256_classic", SecurityLevel::HEStd_256_classic)
+        .value("HEStd_NotSet", SecurityLevel::HEStd_NotSet);
 
+    
     //Parameters Type
+    /*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");
 }
@@ -221,12 +254,12 @@ void bind_ciphertext(py::module &m)
     py::class_<CiphertextImpl<DCRTPoly>, std::shared_ptr<CiphertextImpl<DCRTPoly>>>(m, "Ciphertext")
         .def(py::init<>())
         .def("__add__", [](const Ciphertext<DCRTPoly> &a, const Ciphertext<DCRTPoly> &b)
-             {return a + b; },py::is_operator(),pybind11::keep_alive<0, 1>());
+             {return a + b; },py::is_operator(),pybind11::keep_alive<0, 1>())
        // .def(py::self + py::self);
     // .def("GetDepth", &CiphertextImpl<DCRTPoly>::GetDepth)
     // .def("SetDepth", &CiphertextImpl<DCRTPoly>::SetDepth)
-    // .def("GetLevel", &CiphertextImpl<DCRTPoly>::GetLevel)
-    // .def("SetLevel", &CiphertextImpl<DCRTPoly>::SetLevel)
+     .def("GetLevel", &CiphertextImpl<DCRTPoly>::GetLevel)
+     .def("SetLevel", &CiphertextImpl<DCRTPoly>::SetLevel);
     // .def("GetHopLevel", &CiphertextImpl<DCRTPoly>::GetHopLevel)
     // .def("SetHopLevel", &CiphertextImpl<DCRTPoly>::SetHopLevel)
     // .def("GetScalingFactor", &CiphertextImpl<DCRTPoly>::GetScalingFactor)
@@ -235,6 +268,15 @@ void bind_ciphertext(py::module &m)
     // .def("SetSlots", &CiphertextImpl<DCRTPoly>::SetSlots);
 }
 
+void bind_schemes(py::module &m){
+    /*Bind schemes specific functionalities like bootstrapping functions and multiparty*/
+    py::class_<FHECKKSRNS>(m, "FHECKKSRNS")
+        .def(py::init<>())
+        //.def_static("GetBootstrapDepth", &FHECKKSRNS::GetBootstrapDepth)
+        .def_static("GetBootstrapDepth", static_cast<uint32_t (*)(uint32_t, const std::vector<uint32_t>&, SecretKeyDist)>(&FHECKKSRNS::GetBootstrapDepth));                               
+    
+}
+
 PYBIND11_MODULE(openfhe, m)
 {
     m.doc() = "Open-Source Fully Homomorphic Encryption Library";
@@ -246,4 +288,5 @@ PYBIND11_MODULE(openfhe, m)
     bind_ciphertext(m);
     bind_decryption(m);
     bind_serialization(m);
+    bind_schemes(m);
 }

+ 1 - 1
src/pke/cryptocontext_wrapper.cpp

@@ -1,6 +1,6 @@
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
-#include <openfhe/pke/openfhe.h>
+#include <openfhe.h>
 #include <vector>
 #include <algorithm>
 #include <complex> 

+ 72 - 0
src/pke/examples/simple-ckks-bootstrapping.py

@@ -0,0 +1,72 @@
+from openfhe import *
+
+def main():
+    SimpleBootstrapExample()
+
+def SimpleBootstrapExample():
+    parameters = CCParamsCKKSRNS()
+
+    secretKeyDist = SecretKeyDist.UNIFORM_TERNARY
+    parameters.SetSecretKeyDist(secretKeyDist)
+
+    parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet)
+    parameters.SetRingDim(1<<12)
+
+    rescaleTech = ScalingTechnique.FLEXIBLEAUTO
+    dcrtBits = 59
+    firstMod = 60
+    
+    parameters.SetScalingModSize(dcrtBits)
+    parameters.SetScalingTechnique(rescaleTech)
+    parameters.SetFirstModSize(firstMod)
+
+    levelBudget = [4, 4]
+    approxBootstrappDepth = 8
+
+    levelsUsedBeforeBootstrap = 10
+
+    depth = levelsUsedBeforeBootstrap + FHECKKSRNS.GetBootstrapDepth(approxBootstrappDepth, levelBudget, secretKeyDist)
+
+    parameters.SetMultiplicativeDepth(depth)
+
+    cryptocontext = GenCryptoContext(parameters)
+    cryptocontext.Enable(PKESchemeFeature.PKE)
+    cryptocontext.Enable(PKESchemeFeature.KEYSWITCH)
+    cryptocontext.Enable(PKESchemeFeature.LEVELEDSHE)
+    cryptocontext.Enable(PKESchemeFeature.ADVANCEDSHE)
+    cryptocontext.Enable(PKESchemeFeature.FHE)
+
+    ringDim = cryptocontext.GetRingDimension()
+    # This is the mazimum number of slots that can be used full packing.
+
+    numSlots = int(ringDim / 2)
+    print(f"CKKS is using ring dimension {ringDim}")
+
+    cryptocontext.EvalBootstrapSetup(levelBudget)
+
+    keyPair = cryptocontext.KeyGen()
+    cryptocontext.EvalMultKeyGen(keyPair.secretKey)
+    cryptocontext.EvalBootstrapKeyGen(keyPair.secretKey, numSlots)
+
+    x = [0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0]
+    encodedLength = len(x)
+
+    ptxt = cryptocontext.MakeCKKSPackedPlaintext(x)
+    ptxt.SetLength(encodedLength)
+
+    print(f"Input: {x}")
+
+    ciph = cryptocontext.Encrypt(keyPair.publicKey, ptxt)
+
+    print(f"Initial number of levels remaining: {ciph.GetLevel()}")
+
+    ciphertextAfter = cryptocontext.EvalBootstrap(ciph)
+
+    print(f"Number of levels remaining after bootstrapping: {ciphertextAfter.GetLevel()}")
+
+    result = Decrypt(ciphertextAfter,keyPair.secretKey)
+    result.SetLength(encodedLength)
+    print(f"Output after bootstrapping: {result}")
+
+if __name__ == '__main__':
+    main()