├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── NN ├── include │ ├── ActivationFunctions.h │ ├── CRT.h │ ├── Dataset.h │ ├── Layer.h │ ├── Network.h │ ├── PrebuildNetworks.h │ ├── Tensor.h │ ├── Utils.h │ ├── layers │ │ ├── Activation.h │ │ ├── AveragePool2D.h │ │ ├── Convolve2D.h │ │ ├── Flatten.h │ │ ├── Linear.h │ │ └── Polys.h │ └── nlohmann │ │ └── json.hpp ├── main.cpp └── src │ ├── ActivationFunctions.cpp │ ├── CRT.cpp │ ├── Dataset.cpp │ ├── Network.cpp │ ├── PrebuildNetworks.cpp │ ├── Tensor.cpp │ ├── Utils.cpp │ └── layers │ ├── Activation.cpp │ ├── AveragePool2D.cpp │ ├── Convolve2d.cpp │ └── Linear.cpp ├── README.md ├── fbs ├── include │ ├── definitions.h │ ├── fbscontext.h │ ├── fhew.h │ ├── lwe.h │ ├── lwecore.h │ ├── ringcore.h │ └── ringswitching.h └── src │ ├── fbs.cpp │ ├── fbscontext.cpp │ ├── fhew.cpp │ ├── lwe.cpp │ └── ringswitching.cpp ├── nn_data ├── MNIST_1_6 │ ├── config.json │ ├── dataset │ │ ├── mnist_100_images.csv │ │ └── mnist_100_labels.csv │ └── layers │ │ ├── L0_dense_B.csv │ │ ├── L0_dense_W.csv │ │ ├── L1_dense_B.csv │ │ ├── L1_dense_W.csv │ │ ├── L2_dense_B.csv │ │ └── L2_dense_W.csv └── MNIST_2_4_T1 │ ├── config.json │ ├── dataset │ ├── mnist_100_images.csv │ └── mnist_100_labels.csv │ └── layers │ ├── L0_conv2d_B.csv │ ├── L0_conv2d_W.csv │ ├── L2_conv2d_B.csv │ ├── L2_conv2d_W.csv │ ├── L4_conv2d_B.csv │ ├── L4_conv2d_W.csv │ ├── L6_dense_B.csv │ ├── L6_dense_W.csv │ ├── L7_dense_B.csv │ └── L7_dense_W.csv └── test ├── include ├── LUT.h ├── error.h ├── functions.h ├── implementation.h └── setup.h ├── main.cpp └── src ├── LUT.cpp ├── error.cpp ├── functions.cpp └── implementation.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .idea/ 3 | install/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "palisade-release"] 2 | path = palisade-release 3 | url = https://gitlab.com/palisade/palisade-release.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(FBS) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | include(ExternalProject) 7 | 8 | set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install) 9 | 10 | set(DEBUG OFF) 11 | 12 | ## Build PALISADE 13 | ExternalProject_Add(PALISADE 14 | DOWNLOAD_COMMAND "" 15 | SOURCE_DIR ${PROJECT_SOURCE_DIR}/palisade-release 16 | CMAKE_ARGS 17 | -DWITH_NATIVEOPT=ON 18 | -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} 19 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 20 | -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_SOURCE_DIR}/install 21 | ) 22 | 23 | ExternalProject_Get_Property(PALISADE install_dir) 24 | 25 | include_directories(${PROJECT_SOURCE_DIR}/install/include/palisade) 26 | include_directories(${PROJECT_SOURCE_DIR}/install/include/palisade/core) 27 | 28 | include_directories(${PROJECT_SOURCE_DIR}/fbs/include/) 29 | include_directories(${PROJECT_SOURCE_DIR}/test/include) 30 | 31 | ## Compile definition for benchmarks 32 | #add_compile_definitions(MEASURE_TIME) 33 | 34 | if (DEBUG) 35 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fopenmp") 36 | ## In debug mode it is helpful to store the ring secret key, so we can decrypt RLWE/RGSW samples 37 | add_compile_definitions(WITH_SECRET_KEY) 38 | else() 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -march=native -fopenmp") 40 | ## In debug mode we disable noise, so computations are exact and such that implementation related mistakes 41 | ## are easier to debug 42 | add_compile_definitions(WITH_NOISE) 43 | endif () 44 | 45 | set(PROJECT_SOURCES fbs/src/fbscontext.cpp fbs/src/fhew.cpp fbs/src/lwe.cpp fbs/src/ringswitching.cpp fbs/src/fbs.cpp test/include/error.h) 46 | set(TEST_SOURCES test/src/implementation.cpp test/src/functions.cpp test/src/LUT.cpp test/src/error.cpp) 47 | 48 | link_directories(${PROJECT_SOURCE_DIR}/install/lib) 49 | 50 | message(STATUS "DIR is ${install_dir}") 51 | 52 | add_library(FBSLib SHARED ${PROJECT_SOURCES}) 53 | target_link_libraries(FBSLib PALISADEbinfhe PALISADEcore) 54 | 55 | add_executable(FBSTest test/main.cpp ${TEST_SOURCES}) 56 | 57 | target_include_directories(FBSTest PUBLIC ${PROJECT_SOURCE_DIR}/fbs/include/) 58 | target_include_directories(FBSTest PUBLIC ${PROJECT_SOURCE_DIR}/test/include) 59 | 60 | target_link_libraries(FBSTest PUBLIC PALISADEcore FBSLib) 61 | 62 | set(NN_SOURCES NN/main.cpp NN/src/CRT.cpp NN/src/Utils.cpp NN/src/layers/Linear.cpp 63 | NN/src/layers/Convolve2d.cpp NN/src/layers/AveragePool2D.cpp 64 | NN/src/Tensor.cpp NN/src/PrebuildNetworks.cpp NN/src/ActivationFunctions.cpp NN/src/layers/Activation.cpp NN/src/Network.cpp NN/src/Dataset.cpp) 65 | 66 | set(NN_HEADERS NN/include/Layer.h NN/include/Utils.h) 67 | 68 | add_executable(NN ${NN_SOURCES}) 69 | 70 | target_include_directories(NN PUBLIC ${PROJECT_SOURCE_DIR}/fbs/include/) 71 | target_include_directories(NN PUBLIC ${PROJECT_SOURCE_DIR}/test/include) 72 | target_include_directories(NN PUBLIC ${PROJECT_SOURCE_DIR}/NN/include) 73 | 74 | target_link_libraries(NN PUBLIC FBSLib) 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /NN/include/ActivationFunctions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 08.08.21. 3 | // 4 | 5 | #ifndef FBS_ACTIVATIONFUNCTIONS_H 6 | #define FBS_ACTIVATIONFUNCTIONS_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Utils.h" 12 | 13 | namespace NN { 14 | 15 | static uint32_t relu_degree = 0; 16 | static long double relu_coef[] = {0}; 17 | 18 | static uint32_t tanh_degree = 0; 19 | static long double tanh_coef[] = {0.}; 20 | 21 | static uint32_t id_degree = 1; 22 | static long double id_coef[] = {0, 1.}; 23 | 24 | static uint32_t sigmoid_degree = 0; 25 | static long double sigmoid_coef[] = {0}; 26 | 27 | template 28 | inline std::vector SOFTMAX(std::vector input) { 29 | std::vector conv(input.begin(), input.end()); 30 | std::vector raised(input.size()); 31 | std::vector result(input.size()); 32 | 33 | std::transform(conv.begin(), conv.end(), conv.begin(), [](long double in) {return std::exp(in);}); 34 | auto sum = std::accumulate(conv.begin(), conv.end(), (long double)0.); 35 | 36 | std::transform(raised.begin(), raised.end(), result.begin(), [sum](long double in) { return in / sum; } ); 37 | 38 | return result; 39 | } 40 | 41 | template 42 | inline T RELU(T input, T cutoff) { 43 | if(input >= cutoff) 44 | return T(0); 45 | else 46 | return input; 47 | } 48 | 49 | template 50 | inline T TANH(T input) { 51 | long double conv = input; 52 | return T(tanhl(conv)); 53 | } 54 | 55 | template 56 | inline T SIGMOID(T input) { 57 | long double conv = input; 58 | return T(1. / (1. + std::exp(-conv))); 59 | } 60 | 61 | template 62 | inline T RELU_POLY(T input) { 63 | long double conv = input; 64 | return evaluate_horner(conv, relu_coef, relu_degree); 65 | } 66 | 67 | template 68 | inline T TANH_POLY(T input) { 69 | long double conv = input; 70 | return evaluate_horner(conv, tanh_coef, tanh_degree); 71 | } 72 | 73 | template 74 | inline T SIGMOID_POLY(T input) { 75 | long double conv = input; 76 | return evaluate_horner(conv, sigmoid_coef, sigmoid_degree); 77 | } 78 | } 79 | 80 | #endif //FBS_ACTIVATIONFUNCTIONS_H 81 | -------------------------------------------------------------------------------- /NN/include/CRT.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #ifndef FBS_CRT_H 6 | #define FBS_CRT_H 7 | 8 | #include 9 | 10 | #include "fbscontext.h" 11 | 12 | namespace NN { 13 | 14 | struct PlaintextCRT { 15 | 16 | PlaintextCRT() : moduli(0), contents(0) {} 17 | 18 | explicit PlaintextCRT(std::vector& moduli) : moduli(moduli), contents(moduli.size()) { 19 | 20 | } 21 | 22 | explicit PlaintextCRT(uint64_t value, std::vector& moduli) : moduli(moduli) { 23 | for (auto mod : moduli) { 24 | contents.push_back(value % mod); 25 | } 26 | }; 27 | 28 | PlaintextCRT(std::vector& values, std::vector& moduli) : contents(values), moduli(moduli) { 29 | }; 30 | 31 | PlaintextCRT(const PlaintextCRT& other); 32 | 33 | PlaintextCRT& operator=(PlaintextCRT&& other); 34 | 35 | PlaintextCRT& operator=(const PlaintextCRT& other); 36 | 37 | PlaintextCRT& operator+=(const PlaintextCRT& other); 38 | 39 | friend PlaintextCRT operator+(PlaintextCRT lhs, const PlaintextCRT& other); 40 | 41 | PlaintextCRT& operator-=(const PlaintextCRT& other); 42 | 43 | friend PlaintextCRT operator-(PlaintextCRT lhs, const PlaintextCRT& other); 44 | 45 | PlaintextCRT& operator*=(int64_t coef); 46 | 47 | friend PlaintextCRT operator*(PlaintextCRT lhs, int64_t coef); 48 | 49 | friend std::ostream& operator<<(std::ostream& out, const PlaintextCRT& crt); 50 | 51 | uint64_t at(uint32_t idx) const { 52 | return contents[idx]; 53 | } 54 | 55 | std::vector &GetContents(); 56 | 57 | private: 58 | std::vector contents; 59 | 60 | private: 61 | std::vector moduli; 62 | }; 63 | 64 | /** 65 | * Chinese remainder theorem ciphertext, tuple of LWE ciphertext. Mostly a convenience class 66 | * LWE equivalent of PlaintextCRT 67 | */ 68 | struct CiphertextCRT { 69 | 70 | CiphertextCRT() : contents(0), moduli(0) {}; 71 | 72 | CiphertextCRT(std::vector& contents, std::vector& moduli) : contents(contents), moduli(moduli) {}; 73 | 74 | CiphertextCRT(const CiphertextCRT& other); 75 | 76 | //CiphertextCRT(const CryptoData& data, int msg, const std::vector& moduli); 77 | 78 | CiphertextCRT& operator=(CiphertextCRT&& other) noexcept; 79 | 80 | CiphertextCRT& operator=(const CiphertextCRT& other); 81 | 82 | CiphertextCRT& operator+=(const CiphertextCRT& other); 83 | 84 | friend CiphertextCRT operator+(CiphertextCRT lhs, const CiphertextCRT& other); 85 | 86 | CiphertextCRT& operator-=(const CiphertextCRT& other); 87 | 88 | friend CiphertextCRT operator-(CiphertextCRT lhs, const CiphertextCRT& other); 89 | 90 | CiphertextCRT& operator*=(int64_t coef); 91 | 92 | friend CiphertextCRT operator*(CiphertextCRT lhs, int64_t coef); 93 | 94 | //PlaintextCRT Unpack(const CryptoData& data); 95 | 96 | friend std::ostream& operator<<(std::ostream& stream, const CiphertextCRT& crt); 97 | 98 | friend void swap(CiphertextCRT& lhs, CiphertextCRT rhs) { 99 | using std::swap; 100 | 101 | swap(lhs.contents, rhs.contents); 102 | swap(lhs.moduli, rhs.moduli); 103 | } 104 | 105 | const fbscrypto::LWECiphertext& at(uint32_t idx) const { 106 | return contents[idx]; 107 | } 108 | 109 | private: 110 | std::vector contents; 111 | std::vector moduli; 112 | }; 113 | 114 | } 115 | 116 | #endif //FBS_CRT_H 117 | -------------------------------------------------------------------------------- /NN/include/Dataset.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 11.08.21. 3 | // 4 | 5 | #ifndef FBS_DATASET_H 6 | #define FBS_DATASET_H 7 | 8 | #include "Tensor.h" 9 | #include "Utils.h" 10 | #include 11 | 12 | namespace NN { 13 | std::vector> read_plain_mnist100_1d(std::vector& moduli, std::string& path); 14 | 15 | std::vector> read_encrypted_mnist100_1d(CryptoData& data, std::string& path); 16 | 17 | std::vector> read_plain_mnist100_3d(std::vector& moduli, std::string& path); 18 | 19 | std::vector> read_encrypted_mnist100_3d(CryptoData& data, std::string& path); 20 | 21 | std::vector read_mnist100_labels(std::string& path); 22 | 23 | } 24 | 25 | #endif //FBS_DATASET_H 26 | -------------------------------------------------------------------------------- /NN/include/Layer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #ifndef FBS_LAYER_H 6 | #define FBS_LAYER_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Tensor.h" 13 | 14 | namespace NN { 15 | 16 | namespace Layers { 17 | 18 | enum PADDING { 19 | VALID, 20 | SAME 21 | }; 22 | 23 | template 24 | class TLayer { 25 | 26 | public: 27 | 28 | TLayer(std::vector moduli, uint64_t scale = 0, uint64_t scale_inverse = 0) 29 | : moduli(std::move(moduli)), scale(scale), scale_inverse(scale_inverse) {}; 30 | 31 | virtual void build_from_path(std::vector &paths) = 0; 32 | 33 | virtual std::shared_ptr forward(std::shared_ptr &input) = 0; 34 | 35 | protected: 36 | 37 | uint64_t scale, scale_inverse; 38 | 39 | std::vector moduli; 40 | 41 | }; 42 | 43 | using PlaintextLayer = TLayer; 44 | using CiphertextLayer = TLayer; 45 | 46 | } 47 | 48 | } 49 | 50 | #endif //FBS_LAYER_H 51 | -------------------------------------------------------------------------------- /NN/include/Network.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #ifndef FBS_NETWORK_H 6 | #define FBS_NETWORK_H 7 | 8 | #include "Layer.h" 9 | #include "layers/Activation.h" 10 | #include "nlohmann/json.hpp" 11 | 12 | using json = nlohmann::json; 13 | 14 | namespace NN { 15 | 16 | enum class LAYER_TYPES { 17 | CONV2D, 18 | AVG2D, 19 | DENSE, 20 | FLATTEN 21 | }; 22 | 23 | LAYER_TYPES resolve_layer(std::string& input); 24 | 25 | Layers::ACTIVATION resolve_activation(std::string& input); 26 | 27 | template 28 | class Network { 29 | 30 | public: 31 | 32 | Network() = default; 33 | 34 | std::shared_ptr run(std::shared_ptr& input) { 35 | for(auto& layer : layers) { 36 | input = layer->forward(input); 37 | 38 | /* 39 | if (std::is_same::value){ 40 | auto pt_tensor = input; 41 | if (pt_tensor->GetDimensions() == 1) { 42 | auto t1d = std::dynamic_pointer_cast(pt_tensor); 43 | std::cout << (*t1d) << std::endl; 44 | } else if (pt_tensor->GetDimensions() == 2) { 45 | auto t2d = std::dynamic_pointer_cast(pt_tensor); 46 | std::cout << (*t2d) << std::endl; 47 | } else { 48 | auto t3d = std::dynamic_pointer_cast(pt_tensor); 49 | std::cout << (*t3d) << std::endl; 50 | } 51 | } else { 52 | 53 | 54 | } 55 | */ 56 | } 57 | 58 | return input; 59 | } 60 | 61 | void add_layer(std::shared_ptr& layer) { 62 | layers.push_back(layer); 63 | } 64 | 65 | const std::vector>& GetLayers() { 66 | return layers; 67 | } 68 | 69 | virtual void build_from_directory(std::string dir) = 0; 70 | 71 | protected: 72 | 73 | std::vector> layers{}; 74 | 75 | }; 76 | 77 | class PlaintextNetwork : public Network { 78 | 79 | public: 80 | 81 | PlaintextNetwork(std::vector& moduli) : moduli(moduli) {}; 82 | 83 | void build_from_directory(std::string dir) override; 84 | 85 | private: 86 | 87 | std::vector moduli; 88 | 89 | }; 90 | 91 | class CiphertextNetwork : public Network { 92 | public: 93 | explicit CiphertextNetwork(CryptoData& data) : data(data) {}; 94 | 95 | void build_from_directory(std::string dir) override; 96 | 97 | private: 98 | 99 | CryptoData& data; 100 | 101 | }; 102 | 103 | 104 | } 105 | 106 | #endif //FBS_NETWORK_H 107 | -------------------------------------------------------------------------------- /NN/include/PrebuildNetworks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 08.08.21. 3 | // 4 | 5 | #ifndef FBS_PREBUILDNETWORKS_H 6 | #define FBS_PREBUILDNETWORKS_H 7 | 8 | #include "Network.h" 9 | #include "Tensor.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace NN { 15 | 16 | void run_mnist_1_plain(std::string path, uint64_t modulus, uint32_t n = 20); 17 | 18 | void run_mnist_2_plain(std::string path, uint64_t modulus, uint32_t n = 20); 19 | 20 | void run_mnist_1_encrypted(std::string path, uint64_t modulus, uint32_t paramset_idx, uint32_t n = 20); 21 | 22 | void run_mnist_2_encrypted(std::string path, uint64_t modulus, uint32_t paramset_idx, uint32_t n = 20); 23 | 24 | } 25 | 26 | #endif //FBS_PREBUILDNETWORKS_H 27 | -------------------------------------------------------------------------------- /NN/include/Tensor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #ifndef FBS_TENSOR_H 6 | #define FBS_TENSOR_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Utils.h" 12 | #include "CRT.h" 13 | 14 | namespace NN { 15 | 16 | // Refractor tensors so that only ONE copy of moduli is stored instead of ONE per element 17 | // EDIT: Nope, more work than uses 18 | template 19 | class TTensor { 20 | 21 | public: 22 | 23 | TTensor() = default; 24 | 25 | virtual const IType& at(std::vector idx) = 0; 26 | 27 | virtual uint32_t GetDimensions() { 28 | return 0; 29 | }; 30 | }; 31 | 32 | template 33 | class TTensor1D : public TTensor { 34 | 35 | public: 36 | 37 | explicit TTensor1D() : TTensor(), shape(0) {} 38 | 39 | explicit TTensor1D(std::vector& values) : TTensor(), shape(values.size()), data(values) {} 40 | 41 | const IType& at(std::vector idx) override { 42 | return data[idx[0]]; 43 | } 44 | 45 | IType& operator[](uint32_t idx) { 46 | return data[idx]; 47 | } 48 | 49 | void append(IType& new_end) { 50 | data.push_back(new_end); 51 | shape++; 52 | } 53 | 54 | void insert(uint32_t idx, IType& new_element) { 55 | data.insert(data.begin() + idx, new_element); 56 | shape++; 57 | } 58 | 59 | void append(std::vector& new_end) { 60 | data.insert(data.end(), new_end.begin(), new_end.end()); 61 | shape = data.size(); 62 | } 63 | 64 | void insert(uint32_t idx, std::vector& new_element) { 65 | data.insert(data.begin() + idx, new_element.begin(), new_element.end()); 66 | shape = data.size(); 67 | } 68 | 69 | uint32_t& GetShape() { 70 | return shape; 71 | } 72 | 73 | friend std::ostream& operator<<(std::ostream& stream, const TTensor1D& elem) { 74 | stream << "\t\t[ "; 75 | for(int i = 0; i < elem.data.size() - 1; i++) { 76 | stream << elem.data[i] << ", "; 77 | } 78 | stream << elem.data[elem.shape - 1] << " ]"; 79 | return stream; 80 | } 81 | 82 | uint32_t GetDimensions() override { 83 | return 1; 84 | } 85 | 86 | private: 87 | 88 | uint32_t shape; 89 | std::vector data; 90 | 91 | }; 92 | 93 | 94 | template 95 | class TTensor2D : public TTensor { 96 | public: 97 | explicit TTensor2D() : TTensor(), shapeX(0), shapeY(0) {} 98 | 99 | explicit TTensor2D(std::vector>& values) : TTensor(), shapeX(values[0].size()), shapeY(values.size()) { 100 | for(auto& row : values) { 101 | data.emplace_back(row); 102 | } 103 | } 104 | 105 | const IType& at(std::vector idx) override { 106 | return data[idx[1]][idx[0]]; 107 | } 108 | 109 | TTensor1D& operator[](uint32_t idx) { 110 | return data[idx]; 111 | } 112 | 113 | std::pair GetShape() { 114 | return std::make_pair(shapeX, shapeY); 115 | } 116 | 117 | void insert(uint32_t idx, TTensor1D& new_member) { 118 | auto shape = new_member.GetShape(); 119 | if (shape != shapeX) { 120 | throw std::invalid_argument("Inserted Member must have same shape as other members"); 121 | } 122 | 123 | data.insert(data.begin() + idx, new_member); 124 | shapeY++; 125 | } 126 | 127 | void insert(uint32_t idx, std::vector>& new_members) { 128 | auto shape = new_members[0].GetShape(); 129 | if (shape != shapeX) { 130 | throw std::invalid_argument("Inserted Member must have same shape as other members"); 131 | } 132 | 133 | data.insert(data.begin() + idx, new_members.begin(), new_members.end()); 134 | shapeY += new_members.size(); 135 | } 136 | 137 | void append(TTensor1D new_member) { 138 | auto shape = new_member.GetShape(); 139 | if (shape != shapeX) { 140 | throw std::invalid_argument("Inserted Member must have same shape as other members"); 141 | } 142 | 143 | data.push_back(new_member); 144 | shapeY++; 145 | } 146 | 147 | void append(std::vector>& new_members) { 148 | auto shape = new_members[0].GetShape(); 149 | if (shape != shapeX) { 150 | throw std::invalid_argument("Inserted Member must have same shape as other members"); 151 | } 152 | 153 | data.insert(data.end(), new_members.begin(), new_members.end()); 154 | shapeY += new_members.size(); 155 | } 156 | 157 | uint32_t& GetShapeX() { return shapeX; } 158 | 159 | uint32_t& GetShapeY() {return shapeY; } 160 | 161 | void PropagateShapeChange() { 162 | for(auto& elem : data) { 163 | elem.GetShape() = shapeX; 164 | } 165 | } 166 | 167 | friend std::ostream& operator<<(std::ostream& stream, const TTensor2D& tensor) { 168 | stream << "\t[" << std::endl; 169 | for(auto& elem : tensor.data) 170 | stream << elem << std::endl; 171 | stream << "\t]"; 172 | return stream; 173 | } 174 | 175 | uint32_t GetDimensions() override { 176 | return 2; 177 | } 178 | 179 | private: 180 | 181 | uint32_t shapeX, shapeY; 182 | std::vector> data; 183 | 184 | }; 185 | 186 | template 187 | class TTensor3D : public TTensor { 188 | public: 189 | explicit TTensor3D() : TTensor(), shapeX(0), shapeY(0), shapeZ(0) {} 190 | 191 | explicit TTensor3D(std::vector >>& values ) : TTensor(), shapeZ(values.size()), shapeY(values[0].size()), shapeX(values[0][0].size()) { 192 | for(auto& block : values) { 193 | data.emplace_back(block); 194 | } 195 | } 196 | 197 | const IType& at(std::vector idx) override { 198 | return data[idx[2]][idx[1]][idx[0]]; 199 | } 200 | 201 | TTensor2D& operator[](uint32_t idx) { 202 | return data[idx]; 203 | } 204 | 205 | std::tuple GetShape() { 206 | return std::make_tuple(shapeX, shapeY, shapeZ); 207 | } 208 | 209 | void insert(uint32_t idx, TTensor2D new_member) { 210 | auto shape = new_member.GetShape(); 211 | if (shape.first != shapeX || shape.second != shapeY) { 212 | throw std::invalid_argument("Inserted member must have same shape as other members !"); 213 | } 214 | 215 | data.insert(idx, new_member); 216 | shapeZ++; 217 | } 218 | 219 | void insert(uint32_t idx, std::vector>& new_members) { 220 | auto shape = new_members[0].GetShape(); 221 | if (shape.first != shapeX || shape.second != shapeY) { 222 | throw std::invalid_argument("Inserted Member must have same shape as other members"); 223 | } 224 | 225 | data.insert(data.begin() + idx, new_members.begin(), new_members.end()); 226 | shapeY += new_members.size(); 227 | } 228 | 229 | void append(uint32_t idx, TTensor2D new_member) { 230 | auto shape = new_member.GetShape(); 231 | if (shape.first != shapeX || shape.second != shapeY) { 232 | throw std::invalid_argument("Inserted member must have same shape as other members !"); 233 | } 234 | 235 | data.push_back(new_member); 236 | shapeZ++; 237 | } 238 | 239 | void append(std::vector>& new_members) { 240 | auto shape = new_members[0].GetShape(); 241 | if (shape.first != shapeX || shape.second != shapeY) { 242 | throw std::invalid_argument("Inserted Member must have same shape as other members"); 243 | } 244 | 245 | data.insert(data.end(), new_members.begin(), new_members.end()); 246 | shapeY += new_members.size(); 247 | } 248 | 249 | uint32_t& GetShapeX() { 250 | return shapeX; 251 | }; 252 | 253 | uint32_t& GetShapeY() { 254 | return shapeY; 255 | } 256 | 257 | uint32_t& GetShapeZ() { 258 | return shapeZ; 259 | } 260 | 261 | void PropagateShapeChange() { 262 | for(auto& elem : data) { 263 | elem.GetShapeX() = shapeX; 264 | elem.GetShapeY() = shapeY; 265 | } 266 | } 267 | 268 | friend std::ostream& operator<<(std::ostream& stream, const TTensor3D& tensor) { 269 | stream << "[" << std::endl; 270 | for(auto& elem : tensor.data) 271 | stream << elem << std::endl; 272 | stream << "]" << std::endl; 273 | return stream; 274 | } 275 | 276 | uint32_t GetDimensions() override { 277 | return 3; 278 | } 279 | 280 | 281 | private: 282 | 283 | uint32_t shapeX, shapeY, shapeZ; 284 | std::vector> data; 285 | 286 | }; 287 | 288 | 289 | 290 | using PlaintextTensor = TTensor; 291 | using CiphertextTensor = TTensor; 292 | 293 | using PlaintextTensor1D = TTensor1D; 294 | using CiphertextTensor1D = TTensor1D; 295 | 296 | using PlaintextTensor2D = TTensor2D; 297 | using CiphertextTensor2D = TTensor2D; 298 | 299 | using PlaintextTensor3D = TTensor3D; 300 | using CiphertextTensor3D = TTensor3D; 301 | 302 | void pad_plaintext_tensor3d(std::vector&, const std::shared_ptr&, 303 | uint32_t pad_t, uint32_t pad_b, uint32_t pad_l, uint32_t pad_r); 304 | 305 | void pad_ciphertext_tensor3d(CryptoData& data, const std::shared_ptr&, 306 | uint32_t pad_t, uint32_t pad_b, uint32_t pad_l, uint32_t pad_r); 307 | 308 | template 309 | std::shared_ptr> move_channels_backward(std::shared_ptr>& tensor3D) { 310 | uint32_t s_z, s_y, s_x; 311 | std::tie(s_x, s_y, s_z) = tensor3D->GetShape(); 312 | 313 | std::vector>> result_vec(s_y); 314 | for(uint32_t j = 0; j < s_y; j++) { 315 | std::vector> y_vec(s_x); 316 | for(uint32_t k = 0; k < s_x; k++) { 317 | std::vector x_vec; 318 | for(uint32_t i = 0; i < s_z; i++) { 319 | x_vec.push_back((*tensor3D)[i][j][k]); 320 | } 321 | y_vec[k] = std::move(x_vec); 322 | } 323 | result_vec[j] = std::move(y_vec); 324 | } 325 | 326 | return std::make_shared>(result_vec); 327 | } 328 | } 329 | 330 | #endif //FBS_TENSOR_H 331 | -------------------------------------------------------------------------------- /NN/include/Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #ifndef FBS_UTILS_H 6 | #define FBS_UTILS_H 7 | 8 | #include "fbscontext.h" 9 | #include "CRT.h" 10 | 11 | namespace NN { 12 | 13 | class CryptoData { 14 | 15 | public: 16 | 17 | CryptoData(fbscrypto::FBSFHEPARAMSET set, std::vector& moduli) : ctx(), moduli(moduli) { 18 | 19 | ctx.GenerateFDFHEContext(set); 20 | this->key = ctx.KeyGen(); 21 | ctx.BTKeyGen(this->key); 22 | 23 | } 24 | 25 | fbscrypto::LWECiphertext EncryptNormal(uint64_t value, uint64_t modulus); 26 | 27 | CiphertextCRT EncryptCRT(int64_t value, fbscrypto::CIPHERTEXT_STATE from = fbscrypto::CIPHERTEXT_STATE::TRIVIAL_BEFORE_KEYSWITCH); 28 | 29 | CiphertextCRT DoKeySwitch(const CiphertextCRT& crt); 30 | 31 | CiphertextCRT DoModswitch(const CiphertextCRT& crt); 32 | 33 | CiphertextCRT BootstrapCRT(const CiphertextCRT& input, std::vector& functions, fbscrypto::SKIP_STEP step = fbscrypto::SKIP_STEP::KEYSWITCH); 34 | 35 | CiphertextCRT EncryptCRT(std::vector& values, fbscrypto::CIPHERTEXT_STATE from = fbscrypto::CIPHERTEXT_STATE::TRIVIAL_BEFORE_KEYSWITCH); 36 | 37 | PlaintextCRT DecryptCRT(const CiphertextCRT& value, fbscrypto::CIPHERTEXT_STATE from = fbscrypto::CIPHERTEXT_STATE::TRIVIAL_BEFORE_KEYSWITCH); 38 | 39 | const vector &GetModuli() const; 40 | 41 | private: 42 | 43 | fbscrypto::FBSFHEContext ctx; 44 | fbscrypto::LWEPrivateKey key; 45 | std::vector moduli; 46 | }; 47 | 48 | // function for data input 49 | void read_signed_matrix_from_csv(int64_t* buffer, uint32_t shapeX, uint32_t shapeY, std::string& path); 50 | void read_unsigned_matrix_from_csv(uint64_t* buffer, uint32_t shapeX, uint32_t shapeY, std::string& path); 51 | void read_signed_vector_from_csv(int64_t* buffer, uint32_t shapeY, std::string& path); 52 | void read_unsigned_vector_from_csv(uint64_t* buffer, uint32_t shapeY, std::string& path); 53 | 54 | // evaluate polynomials "efficiently" 55 | long double evaluate_horner(long double input, const long double* coefs, uint64_t size); 56 | 57 | 58 | } 59 | 60 | #endif //FBS_UTILS_H 61 | -------------------------------------------------------------------------------- /NN/include/layers/Activation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 08.08.21. 3 | // 4 | 5 | #ifndef FBS_ACTIVATION_H 6 | #define FBS_ACTIVATION_H 7 | 8 | #include "Layer.h" 9 | #include "Tensor.h" 10 | #include "Utils.h" 11 | 12 | namespace NN { 13 | namespace Layers { 14 | 15 | enum class ACTIVATION { 16 | RELU, 17 | RELU_POLY, 18 | TANH, 19 | TANH_POLY, 20 | SIGMOID, 21 | SIGMOID_POLY, 22 | SOFTMAX, 23 | NONE 24 | }; 25 | 26 | class PlaintextActivation : public PlaintextLayer { 27 | public: 28 | PlaintextActivation(std::vector& moduli, uint64_t scale, uint64_t scale_inv, ACTIVATION activation, bool do_scale = true); 29 | 30 | std::shared_ptr forward(std::shared_ptr& tensor) override; 31 | 32 | void build_from_path(std::vector& paths) override; 33 | 34 | private: 35 | 36 | void compute_activation_1d(shared_ptr &tensor1D); 37 | 38 | void compute_activation_2d(shared_ptr &tensor2D); 39 | 40 | void compute_activation_3d(shared_ptr &tensor3D); 41 | 42 | std::function F; 43 | 44 | protected: 45 | ACTIVATION activation; 46 | }; 47 | 48 | class CiphertextActivation : public CiphertextLayer { 49 | public: 50 | CiphertextActivation(CryptoData& data, uint64_t scale, uint64_t scale_inv, ACTIVATION activation, bool do_scale = true); 51 | 52 | std::shared_ptr forward(std::shared_ptr& tensor) override; 53 | 54 | void build_from_path(std::vector& paths) override; 55 | 56 | private: 57 | 58 | void compute_activation_1d(shared_ptr &tensor1D); 59 | 60 | void compute_activation_2d(shared_ptr &tensor2D); 61 | 62 | void compute_activation_3d(shared_ptr &tensor3D); 63 | 64 | CryptoData& data; 65 | std::vector EF; 66 | protected: 67 | ACTIVATION activation; 68 | }; 69 | } 70 | } 71 | 72 | #endif //FBS_ACTIVATION_H 73 | -------------------------------------------------------------------------------- /NN/include/layers/AveragePool2D.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 07.08.21. 3 | // 4 | 5 | #ifndef FBS_AVERAGEPOOL2D_H 6 | #define FBS_AVERAGEPOOL2D_H 7 | 8 | #include "Layer.h" 9 | #include "Utils.h" 10 | 11 | namespace NN { 12 | namespace Layers { 13 | 14 | class PlaintextAveragePool2D : public PlaintextLayer { 15 | public: 16 | 17 | PlaintextAveragePool2D(std::vector& moduli, uint64_t scale, uint64_t scale_inv, std::pair& pool_size, 18 | std::pair& stride, PADDING pad); 19 | 20 | void build_from_path(std::vector& paths) override; 21 | 22 | std::shared_ptr forward(std::shared_ptr& input) override; 23 | 24 | private: 25 | 26 | std::pair pool_size; 27 | std::pair stride; 28 | PADDING pad; 29 | 30 | }; 31 | 32 | class CiphertextAveragePool2D : public CiphertextLayer { 33 | public: 34 | CiphertextAveragePool2D(CryptoData& data, uint64_t scale, uint64_t scale_inv, std::pair pool_size, 35 | std::pair stride, PADDING pad); 36 | 37 | void build_from_path(std::vector& paths) override; 38 | 39 | std::shared_ptr forward(std::shared_ptr& input) override; 40 | 41 | private: 42 | 43 | CryptoData& data; 44 | std::pair pool_size; 45 | std::pair stride; 46 | PADDING pad; 47 | 48 | }; 49 | 50 | } 51 | } 52 | 53 | #endif //FBS_AVERAGEPOOL2D_H 54 | -------------------------------------------------------------------------------- /NN/include/layers/Convolve2D.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Leonard on 8/4/21. 3 | // 4 | 5 | #ifndef FBS_CONVOLVE2D_H 6 | #define FBS_CONVOLVE2D_H 7 | 8 | #include "Layer.h" 9 | #include "Utils.h" 10 | #include "Activation.h" 11 | 12 | namespace NN { 13 | // TODO: BasicActivation for N-D inputs 14 | 15 | /* 16 | * How conv layers should be exported 17 | * using layer.get_weight() will yield a tensor of shape [m,n,o,p] 18 | * where an entry [i,j,k,l] is coefficient [i,j,k] of the l-th filter. 19 | * 20 | * Exporting a layer shall yield two files 21 | * 22 | * 1] a file containing the biases: 23 | * Line I of the file will contain the bias of filter I 24 | * 2] a file containing the filters: 25 | * Line I of the file will contain the vector stored in the filter at index [i,j,k] such that 26 | * I = k + j * o + i * o * m. In practice this means we can dump the filters using the following pseudocode 27 | * for i in range(m): 28 | * for j in range(n): 29 | * for k in range(o): 30 | * file.writeline(layer.get_weight()[i,j,k] 31 | */ 32 | 33 | namespace Layers { 34 | 35 | 36 | class PlaintextConvolve2D : public PlaintextActivation { 37 | 38 | public: 39 | PlaintextConvolve2D(std::vector &moduli, uint64_t scale, uint64_t scale_inv, uint32_t n_channels, uint32_t n_filters, 40 | std::pair &kernel_size, std::pair &stride, 41 | PADDING pad, ACTIVATION activation); 42 | 43 | std::shared_ptr forward(std::shared_ptr& input) override; 44 | 45 | void build_from_path(std::vector& paths) override; 46 | 47 | private: 48 | 49 | uint32_t n_filters, n_channels; 50 | std::vector bias; 51 | std::vector> data; 52 | PADDING pad; 53 | std::pair kernel_size, stride; 54 | 55 | }; 56 | 57 | class CiphertextConvolve2D : public CiphertextActivation { 58 | public: 59 | CiphertextConvolve2D(CryptoData& CData, uint64_t scale, uint64_t scale_inv, uint32_t n_channels, uint32_t n_filters, std::pair& kernel_size, std::pair& stride, PADDING pad, 61 | ACTIVATION activation); 62 | 63 | std::shared_ptr forward(std::shared_ptr& input) override; 64 | 65 | void build_from_path(std::vector& path) override; 66 | 67 | private: 68 | 69 | CryptoData& Cdata; 70 | 71 | uint32_t n_filters, n_channels; 72 | std::vector bias; 73 | std::vector> data; 74 | PADDING pad; 75 | std::pair kernel_size, stride; 76 | 77 | }; 78 | } 79 | } 80 | 81 | #endif //FBS_CONVOLVE2D_H 82 | -------------------------------------------------------------------------------- /NN/include/layers/Flatten.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 08.08.21. 3 | // 4 | 5 | #ifndef FBS_FLATTEN_H 6 | #define FBS_FLATTEN_H 7 | 8 | #include "Layer.h" 9 | 10 | namespace NN { 11 | namespace Layers { 12 | 13 | template 14 | class Flatten : public TLayer> { 15 | 16 | public: 17 | Flatten() : TLayer>({}) {} 18 | 19 | void build_from_path(std::vector& path) override { 20 | throw std::runtime_error("Build from path is unnecessary for Flatten layer"); 21 | } 22 | 23 | std::shared_ptr> flatten2d(std::shared_ptr>& input) { 24 | auto shape = input->GetShape(); 25 | std::vector result; 26 | for(uint32_t i = 0; i < shape.second; i++) { 27 | for(uint32_t j = 0; j < shape.first; j++) { 28 | // Todo: use move constructor 29 | result.emplace_back((*input)[i][j]); 30 | } 31 | } 32 | 33 | return std::make_shared>(result); 34 | } 35 | 36 | std::shared_ptr> flatten3d(std::shared_ptr>& tensor) { 37 | uint32_t shapeX, shapeY,shapeZ; 38 | auto input = move_channels_backward(tensor); 39 | std::tie(shapeX, shapeY, shapeZ) = input->GetShape(); 40 | 41 | std::vector result; 42 | for(uint32_t i = 0; i < shapeZ; i++) { 43 | for(uint32_t j = 0; j < shapeY; j++) { 44 | for(uint32_t k = 0; k < shapeX; k++) { 45 | // todo: use move constructor 46 | result.emplace_back((*input)[i][j][k]); 47 | } 48 | } 49 | } 50 | 51 | return std::make_shared>(result); 52 | } 53 | 54 | std::shared_ptr> forward(std::shared_ptr>& input) override { 55 | 56 | std::shared_ptr> tensor2D; 57 | std::shared_ptr> tensor3D; 58 | 59 | if((tensor2D = std::dynamic_pointer_cast>(input)) != nullptr) { 60 | return flatten2d(tensor2D); 61 | } else if((tensor3D = std::dynamic_pointer_cast>(input)) != nullptr) { 62 | return flatten3d(tensor3D); 63 | } else { 64 | // in this case we have a 1D tensor, so no need to do anything; 65 | return input; 66 | } 67 | 68 | } 69 | }; 70 | 71 | using PlaintextFlatten = Flatten; 72 | using CiphertextFlatten = Flatten; 73 | 74 | } 75 | } 76 | 77 | #endif //FBS_FLATTEN_H 78 | -------------------------------------------------------------------------------- /NN/include/layers/Linear.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #ifndef FBS_LINEAR_H 6 | #define FBS_LINEAR_H 7 | 8 | #include "Layer.h" 9 | #include "Tensor.h" 10 | #include "Utils.h" 11 | 12 | #include "Activation.h" 13 | 14 | namespace NN { 15 | namespace Layers { 16 | 17 | class PlaintextLinear : public PlaintextActivation { 18 | 19 | public: 20 | 21 | PlaintextLinear(std::vector& mod, uint64_t scale, uint64_t scale_inv, uint32_t shapeX, uint32_t shapeY, ACTIVATION activation) : PlaintextActivation(mod, scale, scale_inv, activation), shapeX(shapeX), shapeY(shapeY) { 22 | W_m = std::vector>(shapeY); 23 | B_v = std::vector(shapeX); 24 | for(uint32_t i = 0; i < shapeY; i++) 25 | W_m[i] = std::vector(shapeX); 26 | 27 | } 28 | 29 | std::shared_ptr forward(std::shared_ptr& tens) override; 30 | 31 | void build_from_path(std::vector &paths) override; 32 | 33 | private: 34 | 35 | uint32_t shapeX, shapeY; 36 | std::vector> W_m; 37 | std::vector B_v; 38 | 39 | }; 40 | 41 | class CiphertextLinear : public CiphertextActivation { 42 | 43 | public: 44 | 45 | CiphertextLinear(CryptoData& data, uint64_t scale, uint64_t scale_inv, uint32_t shapeX, uint32_t shapeY, ACTIVATION activation) : CiphertextActivation(data, scale, scale_inv, activation), CryptData(data), shapeX(shapeX), shapeY(shapeY) { 46 | W_m = std::vector>(shapeY); 47 | B_v = std::vector(shapeX); 48 | for(uint32_t i = 0; i < shapeY; i++) 49 | W_m[i] = std::vector(shapeX); 50 | 51 | } 52 | 53 | std::shared_ptr forward(std::shared_ptr& tens) override; 54 | 55 | void build_from_path(std::vector &paths) override; 56 | 57 | private: 58 | 59 | CryptoData& CryptData; 60 | uint32_t shapeX, shapeY; 61 | std::vector> W_m; 62 | std::vector B_v; 63 | }; 64 | 65 | } 66 | } 67 | 68 | #endif //FBS_LINEAR_H 69 | -------------------------------------------------------------------------------- /NN/include/layers/Polys.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #ifndef FBS_POLYS_H 6 | #define FBS_POLYS_H 7 | 8 | static uint32_t relu_degree = 0; 9 | static uint64_t relu_coef[] = {0}; 10 | 11 | static uint32_t tanh_degree = 0; 12 | static uint64_t tanh_coef[] = {0}; 13 | 14 | static uint32_t id_degree = 1; 15 | static uint64_t id_coef[] = {0, 10}; 16 | 17 | #endif //FBS_POLYS_H 18 | -------------------------------------------------------------------------------- /NN/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #include 6 | #include "Utils.h" 7 | #include "PrebuildNetworks.h" 8 | 9 | #define TICK std::chrono::high_resolution_clock::now(); 10 | 11 | using namespace NN; 12 | 13 | 14 | int main() { 15 | 16 | std::cout << std::endl << "### RUNNING NEURAL NETWORK EVALUATION ###" << std::endl; 17 | std::cout << "## TESTING MNIST 1 ##" << std::endl; 18 | 19 | for(uint32_t m = 6; m <= 11; m++) { 20 | std::cout << "[Using modulus = " << ((1 << m)) << "]" << std::endl; 21 | for(uint32_t i = 0; i < 6; i++) { 22 | run_mnist_1_encrypted("nn_data/MNIST_1_6", 1 << m, i); 23 | } 24 | } 25 | 26 | std::cout << "## TESTING MNIST 2 ##" << std::endl; 27 | for(uint32_t m = 6; m <= 11; m++) { 28 | std::cout << "[Using modulus = " << ((1 << m)) << "]" << std::endl; 29 | for(uint32_t i = 0; i < 6; i++) { 30 | run_mnist_2_encrypted("nn_data/MNIST_2_4_T1", 1 << m, i); 31 | } 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /NN/src/ActivationFunctions.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 08.08.21. 3 | // 4 | 5 | #include "ActivationFunctions.h" -------------------------------------------------------------------------------- /NN/src/CRT.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #include "CRT.h" 6 | 7 | namespace NN { 8 | 9 | #include 10 | 11 | CiphertextCRT::CiphertextCRT(const CiphertextCRT &other) : moduli(other.moduli) { 12 | for (auto& ct : other.contents) { 13 | 14 | NativeVector A = ct->GetA(); 15 | NativeInteger b = ct->GetB(); 16 | 17 | contents.push_back(std::make_shared(A, b)); 18 | } 19 | } 20 | 21 | CiphertextCRT &CiphertextCRT::operator=(CiphertextCRT &&other) noexcept { 22 | this->moduli = std::move(other.moduli); 23 | this->contents = std::move(other.contents); 24 | return *this; 25 | } 26 | 27 | CiphertextCRT & CiphertextCRT::operator=(const CiphertextCRT& other) { 28 | swap(*this, CiphertextCRT(other)); 29 | 30 | return *this; 31 | } 32 | 33 | CiphertextCRT& CiphertextCRT::operator+=(const CiphertextCRT &other) { 34 | for(uint32_t i = 0; i < contents.size(); i++) { 35 | (*contents[i]) += (*other.contents[i]); 36 | } 37 | return *this; 38 | } 39 | 40 | CiphertextCRT& CiphertextCRT::operator-=(const CiphertextCRT &other) { 41 | for(uint32_t i = 0; i < contents.size(); i++) { 42 | (*contents[i]) -= (*other.contents[i]); 43 | } 44 | return *this; 45 | } 46 | 47 | CiphertextCRT& CiphertextCRT::operator*=(const int64_t scale) { 48 | for(uint32_t i = 0; i < contents.size(); i++) { 49 | uint32_t rem = scale % moduli[i]; 50 | int32_t actual_scale = scale < 0 ? int32_t(rem) - int32_t(moduli[i]) : int32_t(rem); 51 | (*contents[i]) *= actual_scale; 52 | } 53 | return *this; 54 | } 55 | 56 | CiphertextCRT operator+(CiphertextCRT lhs, const CiphertextCRT &other) { 57 | lhs += other; 58 | return lhs; 59 | } 60 | 61 | CiphertextCRT operator-(CiphertextCRT lhs, const CiphertextCRT &other) { 62 | lhs -= other; 63 | return lhs; 64 | } 65 | 66 | CiphertextCRT operator*(CiphertextCRT lhs, const int64_t coef) { 67 | lhs *= coef; 68 | return lhs; 69 | } 70 | 71 | std::ostream& operator<<(std::ostream& stream, const CiphertextCRT& crt) { 72 | stream << "[ "; 73 | for(int i = 0; i < crt.contents.size() - 1; i++) { 74 | stream << 0 << ", "; 75 | } 76 | stream << 0 << " ]"; 77 | 78 | return stream; 79 | } 80 | 81 | PlaintextCRT::PlaintextCRT(const PlaintextCRT &other) : moduli(other.moduli), contents(other.contents) { 82 | 83 | }; 84 | 85 | PlaintextCRT& PlaintextCRT::operator=(PlaintextCRT &&other) { 86 | contents = std::move(other.contents); 87 | moduli = std::move(other.moduli); 88 | return *this; 89 | } 90 | 91 | PlaintextCRT & PlaintextCRT::operator=(const PlaintextCRT& other) { 92 | using std::swap; 93 | PlaintextCRT tmp(other); 94 | swap(contents, tmp.contents); 95 | return *this; 96 | } 97 | 98 | PlaintextCRT & PlaintextCRT::operator+=(const PlaintextCRT &other) { 99 | for (uint32_t i = 0; i < contents.size(); i++) { 100 | contents[i] = (contents[i] + other.contents[i]) % moduli[i]; 101 | } 102 | return *this; 103 | } 104 | 105 | PlaintextCRT & PlaintextCRT::operator-=(const PlaintextCRT &other) { 106 | for (uint32_t i = 0; i < contents.size(); i++) { 107 | uint32_t a = contents[i]; 108 | uint32_t b = other.contents[i]; 109 | 110 | if (a < b) 111 | a += moduli[i]; 112 | 113 | contents[i] = a - b; 114 | } 115 | 116 | return *this; 117 | } 118 | 119 | PlaintextCRT & PlaintextCRT::operator*=(const int64_t coef) { 120 | 121 | uint32_t w = std::abs(coef); 122 | for (uint32_t i = 0; i < contents.size(); i++) { 123 | contents[i] = (contents[i] * (w % moduli[i])) % moduli[i]; 124 | if (coef < 0 && contents[i] > 0) { 125 | contents[i] = moduli[i] - contents[i]; 126 | } 127 | } 128 | 129 | return *this; 130 | } 131 | 132 | PlaintextCRT operator+(PlaintextCRT lhs, const PlaintextCRT& other) { 133 | lhs += other; 134 | return lhs; 135 | } 136 | 137 | PlaintextCRT operator-(PlaintextCRT lhs, const PlaintextCRT& other) { 138 | lhs -= other; 139 | return lhs; 140 | } 141 | 142 | PlaintextCRT operator*(PlaintextCRT lhs, const int64_t coef) { 143 | lhs *= coef; 144 | return lhs; 145 | } 146 | 147 | 148 | std::vector &PlaintextCRT::GetContents() { 149 | return contents; 150 | } 151 | 152 | std::ostream& operator<<(std::ostream& stream, const PlaintextCRT& crt) { 153 | stream << "[ "; 154 | for(int i = 0; i < crt.contents.size() - 1; i++) { 155 | stream << crt.contents[i] << ", "; 156 | } 157 | stream << crt.contents[crt.contents.size() - 1] << " ]"; 158 | 159 | return stream; 160 | } 161 | 162 | } -------------------------------------------------------------------------------- /NN/src/Dataset.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 11.08.21. 3 | // 4 | 5 | #include "Dataset.h" 6 | 7 | namespace NN { 8 | std::vector> read_plain_mnist100_1d(std::vector& moduli, std::string& path) { 9 | constexpr uint32_t rows = 100; 10 | constexpr uint32_t columns = 784; 11 | uint64_t arr[rows * columns]; 12 | 13 | read_unsigned_matrix_from_csv(arr, columns, rows, path); 14 | 15 | std::vector> result(rows); 16 | for(uint32_t i = 0; i < rows; i++) { 17 | std::vector tmp; 18 | for(uint32_t j = 0; j < columns; j++) { 19 | tmp.emplace_back(arr[i * columns + j], moduli); 20 | } 21 | result[i] = std::make_shared(tmp); 22 | } 23 | return result; 24 | } 25 | 26 | std::vector> read_encrypted_mnist100_1d(CryptoData& data, std::string& path) { 27 | 28 | std::vector moduli = data.GetModuli(); 29 | auto tmp = read_plain_mnist100_1d(moduli, path); 30 | std::vector> results; 31 | for(auto& v : tmp) { 32 | std::vector entry; 33 | for(uint32_t i = 0; i < v->GetShape(); i++) { 34 | entry.push_back(data.EncryptCRT((*v)[i].GetContents(), fbscrypto::CIPHERTEXT_STATE::TRIVIAL_BEFORE_KEYSWITCH)); 35 | } 36 | results.push_back(std::make_shared(entry)); 37 | } 38 | 39 | return results; 40 | } 41 | 42 | std::vector> read_plain_mnist100_3d(std::vector& moduli, std::string& path) { 43 | constexpr uint32_t rows = 100; 44 | constexpr uint32_t columns = 784; 45 | uint64_t arr[rows * columns]; 46 | 47 | read_unsigned_matrix_from_csv(arr, columns, rows, path); 48 | 49 | std::vector> elements; 50 | for(uint32_t i = 0; i < rows; i++) { 51 | std::vector>> new_sample(1); 52 | new_sample[0] = std::vector>(28); 53 | for(uint32_t c_row = 0; c_row < 28; c_row++) { 54 | std::vector current_row; 55 | for(uint32_t c_col = 0; c_col < 28; c_col++) { 56 | current_row.emplace_back(arr[i * columns + 28 * c_row + c_col], moduli); 57 | } 58 | new_sample[0][c_row] = std::move(current_row); 59 | } 60 | elements.emplace_back(std::make_shared(new_sample)); 61 | } 62 | 63 | return elements; 64 | } 65 | 66 | std::vector> read_encrypted_mnist100_3d(CryptoData& data, std::string& path) { 67 | auto mods = data.GetModuli(); 68 | auto tmp_data = read_plain_mnist100_3d(mods, path); 69 | 70 | std::vector> results; 71 | for(auto& v : tmp_data) { 72 | std::vector>> entry(1); 73 | entry[0] = std::vector>(28); 74 | for(uint32_t i = 0; i < 28; i++) { 75 | std::vector row; 76 | for(uint32_t j = 0; j < 28; j++) { 77 | row.emplace_back(data.EncryptCRT((*v)[0][i][j].GetContents())); 78 | } 79 | entry[0][i] = std::move(row); 80 | } 81 | results.emplace_back(std::make_shared(entry)); 82 | } 83 | 84 | return results; 85 | } 86 | 87 | std::vector read_mnist100_labels(std::string& path) { 88 | 89 | std::vector labels(100); 90 | read_unsigned_vector_from_csv(labels.data(), 100, path); 91 | 92 | return labels; 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /NN/src/Network.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 09.08.21. 3 | // 4 | 5 | #include "Network.h" 6 | #include "layers/Linear.h" 7 | #include "layers/AveragePool2D.h" 8 | #include "layers/Convolve2D.h" 9 | #include "layers/Flatten.h" 10 | 11 | namespace NN { 12 | 13 | LAYER_TYPES resolve_layer(std::string& input) { 14 | 15 | if (input == "conv2d") 16 | return LAYER_TYPES::CONV2D; 17 | else if (input == "avg_pool2d") 18 | return LAYER_TYPES::AVG2D; 19 | else if (input == "dense") 20 | return LAYER_TYPES::DENSE; 21 | else if (input == "flatten") 22 | return LAYER_TYPES::FLATTEN; 23 | 24 | std::string message = "Invalid layer string: "; 25 | message.append(input); 26 | 27 | throw std::invalid_argument(message); 28 | } 29 | 30 | Layers::ACTIVATION resolve_activation(std::string& input) { 31 | if ((input == "relu") or (input == "relu_modulo")) 32 | return Layers::ACTIVATION::RELU; 33 | if ((input == "softmax") or (input == "softmax_modulo")) 34 | return Layers::ACTIVATION::SOFTMAX; 35 | if ((input == "tanh") or (input == "tanh_modulo")) 36 | return Layers::ACTIVATION::TANH; 37 | if ((input == "sigmoid") or (input == "sigmoid_modulo")) 38 | return Layers::ACTIVATION::SIGMOID; 39 | 40 | return Layers::ACTIVATION::NONE; 41 | } 42 | 43 | void PlaintextNetwork::build_from_directory(std::string model_dir) { 44 | 45 | std::ifstream config_stream; 46 | if (model_dir.back() != '/') 47 | model_dir.append("/"); 48 | 49 | auto dir = model_dir; 50 | 51 | dir.append("config.json"); 52 | 53 | config_stream.open(dir); 54 | 55 | if (!config_stream) { 56 | std::cerr << "Could not open file: " << dir << std::endl; 57 | std::exit(-1); 58 | } 59 | 60 | auto config = json::parse(config_stream); 61 | 62 | std::vector paths(2); 63 | for(uint32_t i = 0; i < config.size(); i++) { 64 | 65 | auto& current_layer = config[i]; 66 | 67 | std::string layer_name = current_layer["name"]; 68 | 69 | auto layer_type = resolve_layer(layer_name); 70 | 71 | switch (layer_type) { 72 | case LAYER_TYPES::DENSE: { 73 | 74 | std::string activation_str = current_layer["activation"]; 75 | 76 | paths[0] = current_layer["weights"]; 77 | paths[1] = current_layer["bias"]; 78 | 79 | paths[0].insert(0, model_dir); 80 | paths[1].insert(0, model_dir); 81 | 82 | uint32_t shapeX = current_layer["columns"]; 83 | uint32_t shapeY = current_layer["rows"]; 84 | uint64_t scale = current_layer["scale"]; 85 | uint64_t scale_inv = current_layer["scale_inv"]; 86 | 87 | // risky 88 | 89 | auto activation = resolve_activation(activation_str); 90 | 91 | std::shared_ptr dense = 92 | std::make_shared(moduli, scale, scale_inv, shapeX, shapeY, activation); 93 | dense->build_from_path(paths); 94 | 95 | this->layers.push_back(dense); 96 | break; 97 | } 98 | case LAYER_TYPES::FLATTEN: { 99 | std::shared_ptr flatten = std::make_shared(); 100 | this->layers.push_back(flatten); 101 | break; 102 | } 103 | case LAYER_TYPES::AVG2D: { 104 | 105 | Layers::PADDING pad; 106 | if (current_layer["padding"] == "same") 107 | pad = Layers::PADDING::SAME; 108 | else 109 | pad = Layers::PADDING::VALID; 110 | 111 | auto stride = current_layer["strides"]; 112 | auto pool_size = current_layer["pool_size"]; 113 | 114 | auto stride_tuple = std::make_pair(stride[1], stride[0]); 115 | auto pool_size_tuple = std::make_pair(pool_size[1], pool_size[0]); 116 | 117 | uint64_t scale = current_layer["scale"]; 118 | uint64_t scale_inv = current_layer["scale_inv"]; 119 | 120 | 121 | std::shared_ptr pool2D = 122 | std::make_shared(moduli, scale, scale_inv, pool_size_tuple, stride_tuple, pad); 123 | 124 | this->layers.push_back(pool2D); 125 | break; 126 | } 127 | 128 | case LAYER_TYPES::CONV2D: { 129 | std::string activation_str = current_layer["activation"]; 130 | auto activation = resolve_activation(activation_str); 131 | 132 | Layers::PADDING pad; 133 | if (current_layer["padding"] == "same") 134 | pad = Layers::PADDING::SAME; 135 | else 136 | pad = Layers::PADDING::VALID; 137 | 138 | paths[0] = current_layer["weights"]; 139 | paths[1] = current_layer["bias"]; 140 | 141 | paths[0].insert(0, model_dir); 142 | paths[1].insert(0, model_dir); 143 | 144 | uint64_t scale = current_layer["scale"]; 145 | uint64_t scale_inv = current_layer["scale_inv"]; 146 | 147 | 148 | uint32_t n_filters = current_layer["filters"]; 149 | uint32_t channels = current_layer["channels"]; 150 | 151 | auto stride = current_layer["strides"]; 152 | auto pool_size = current_layer["kernel_size"]; 153 | auto stride_tuple = std::make_pair(stride[1], stride[0]); 154 | auto pool_size_tuple = std::make_pair(pool_size[1], pool_size[0]); 155 | 156 | std::shared_ptr conv2D = 157 | std::make_shared(moduli, scale, scale_inv, channels, n_filters, pool_size_tuple, stride_tuple, 158 | pad, activation); 159 | 160 | conv2D->build_from_path(paths); 161 | layers.push_back(conv2D); 162 | break; 163 | } 164 | } 165 | 166 | } 167 | } 168 | 169 | void CiphertextNetwork::build_from_directory(std::string model_dir) { 170 | 171 | std::ifstream config_stream; 172 | 173 | if (model_dir.back() != '/') 174 | model_dir.append("/"); 175 | 176 | auto dir = model_dir; 177 | dir.append("config.json"); 178 | 179 | config_stream.open(dir); 180 | 181 | if (!config_stream) { 182 | std::cerr << "Could not open file: " << dir << std::endl; 183 | std::exit(-1); 184 | } 185 | 186 | auto config = json::parse(config_stream); 187 | 188 | std::vector paths(2); 189 | for(uint32_t i = 0; i < config.size(); i++) { 190 | 191 | auto& current_layer = config[i]; 192 | 193 | std::string layer_name = current_layer["name"]; 194 | 195 | auto layer_type = resolve_layer(layer_name); 196 | 197 | switch (layer_type) { 198 | case LAYER_TYPES::DENSE: { 199 | 200 | std::string activation_str = current_layer["activation"]; 201 | 202 | paths[0] = current_layer["weights"]; 203 | paths[1] = current_layer["bias"]; 204 | 205 | paths[0].insert(0, model_dir); 206 | paths[1].insert(0, model_dir); 207 | 208 | uint32_t shapeX = current_layer["columns"]; 209 | uint32_t shapeY = current_layer["rows"]; 210 | uint64_t scale = current_layer["scale"]; 211 | uint64_t scale_inv = current_layer["scale_inv"]; 212 | 213 | std::vector moduli = data.GetModuli(); 214 | 215 | auto activation = resolve_activation(activation_str); 216 | 217 | std::shared_ptr dense = 218 | std::make_shared(data, scale, scale_inv, shapeX, shapeY, activation); 219 | dense->build_from_path(paths); 220 | 221 | this->layers.push_back(dense); 222 | break; 223 | } 224 | case LAYER_TYPES::FLATTEN: { 225 | std::shared_ptr flatten = std::make_shared(); 226 | this->layers.push_back(flatten); 227 | break; 228 | } 229 | case LAYER_TYPES::AVG2D: { 230 | 231 | Layers::PADDING pad; 232 | if (current_layer["padding"] == "same") 233 | pad = Layers::PADDING::SAME; 234 | else 235 | pad = Layers::PADDING::VALID; 236 | 237 | auto stride = current_layer["strides"]; 238 | auto pool_size = current_layer["pool_size"]; 239 | 240 | auto stride_tuple = std::make_pair(stride[1], stride[0]); 241 | auto pool_size_tuple = std::make_pair(pool_size[1], pool_size[0]); 242 | 243 | uint64_t scale = current_layer["scale"]; 244 | uint64_t scale_inv = current_layer["scale_inv"]; 245 | 246 | 247 | std::shared_ptr pool2D = 248 | std::make_shared(data, scale, scale_inv, pool_size_tuple, stride_tuple, pad); 249 | 250 | this->layers.push_back(pool2D); 251 | break; 252 | } 253 | 254 | case LAYER_TYPES::CONV2D: { 255 | std::string activation_str = current_layer["activation"]; 256 | auto activation = resolve_activation(activation_str); 257 | 258 | Layers::PADDING pad; 259 | if (current_layer["padding"] == "same") 260 | pad = Layers::PADDING::SAME; 261 | else 262 | pad = Layers::PADDING::VALID; 263 | 264 | paths[0] = current_layer["weights"]; 265 | paths[1] = current_layer["bias"]; 266 | 267 | paths[0].insert(0, model_dir); 268 | paths[1].insert(0, model_dir); 269 | 270 | uint64_t scale = current_layer["scale"]; 271 | uint64_t scale_inv = current_layer["scale_inv"]; 272 | 273 | uint32_t n_filters = current_layer["filters"]; 274 | uint32_t channels = current_layer["channels"]; 275 | 276 | auto stride = current_layer["strides"]; 277 | auto pool_size = current_layer["kernel_size"]; 278 | auto stride_tuple = std::make_pair(stride[1], stride[0]); 279 | auto pool_size_tuple = std::make_pair(pool_size[1], pool_size[0]); 280 | 281 | std::shared_ptr conv2D = 282 | std::make_shared(data, scale, scale_inv, channels, n_filters, pool_size_tuple, stride_tuple, 283 | pad, activation); 284 | 285 | conv2D->build_from_path(paths); 286 | layers.push_back(conv2D); 287 | break; 288 | } 289 | } 290 | 291 | } 292 | } 293 | 294 | 295 | } -------------------------------------------------------------------------------- /NN/src/PrebuildNetworks.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 08.08.21. 3 | // 4 | 5 | #include "PrebuildNetworks.h" 6 | #include "Dataset.h" 7 | 8 | #define TICK std::chrono::high_resolution_clock::now() 9 | 10 | namespace NN { 11 | 12 | void run_mnist_1_plain(std::string path, uint64_t modulus, uint32_t n) { 13 | 14 | std::vector moduli = {modulus}; 15 | 16 | if (path[path.size() - 1] != '/') 17 | path.append("/"); 18 | 19 | auto mnist_images_path = path + "dataset/mnist_100_images.csv"; 20 | auto mnist_labels_path = path + "dataset/mnist_100_labels.csv"; 21 | 22 | // import images 23 | auto mnist_images = read_plain_mnist100_1d(moduli, mnist_images_path); 24 | auto mnist_labels = read_mnist100_labels(mnist_labels_path); 25 | 26 | PlaintextNetwork nn(moduli); 27 | nn.build_from_directory(path); 28 | 29 | auto start = TICK; 30 | 31 | std::vector results; 32 | 33 | for(uint32_t j = 0; j < n; j++) { 34 | 35 | auto& img = mnist_images[j]; 36 | std::shared_ptr tensor = img; 37 | auto result = nn.run(tensor); 38 | for(uint32_t i = 0; i < 10; i++) { 39 | auto elem = result->at({i}); 40 | if (elem.at(0) == 1ull) { 41 | results.push_back(i); 42 | break; 43 | } 44 | } 45 | } 46 | 47 | auto stop = TICK; 48 | 49 | double correct_count = 0; 50 | for(uint32_t i = 0; i < n; i++) { 51 | if (results[i] == mnist_labels[i]) 52 | correct_count += 1; 53 | } 54 | auto elapsed = std::chrono::duration_cast(stop-start).count(); 55 | 56 | std::cout << "[PLAINTEXT] Evaluation accuracy is " << correct_count / n << " " << std::endl; 57 | std::cout << "The evaluation of" << n << "samples took " << elapsed << "seconds" << std::endl; 58 | } 59 | 60 | void run_mnist_1_encrypted(std::string path, uint64_t modulus, uint32_t paramset_idx, uint32_t n) { 61 | 62 | std::vector moduli = {modulus}; 63 | 64 | CryptoData data(fbscrypto::FBSFHEPARAMSET_LIST[paramset_idx], moduli); 65 | 66 | if (path[path.size() - 1] != '/') 67 | path.append("/"); 68 | 69 | auto mnist_images_path = path + "dataset/mnist_100_images.csv"; 70 | auto mnist_labels_path = path + "dataset/mnist_100_labels.csv"; 71 | 72 | // import images 73 | auto mnist_images = read_encrypted_mnist100_1d(data, mnist_images_path); 74 | auto mnist_labels = read_mnist100_labels(mnist_labels_path); 75 | 76 | CiphertextNetwork nn(data); 77 | nn.build_from_directory(path); 78 | 79 | auto start = TICK; 80 | 81 | std::vector results(n); 82 | 83 | for(uint32_t ctr = 0; ctr < n; ctr++) { 84 | std::shared_ptr tensor = mnist_images[ctr]; 85 | auto result = nn.run(tensor); 86 | for(uint32_t i = 0; i < 10; i++) { 87 | auto enc_elem = result->at({i}); 88 | auto elem = data.DecryptCRT(enc_elem); 89 | if (elem.at(0) >= 1ull) { 90 | results[ctr] = i; 91 | break; 92 | } 93 | } 94 | } 95 | 96 | auto stop = TICK; 97 | 98 | double correct_count = 0; 99 | for(uint32_t i = 0; i < n; i++) { 100 | if (results[i] == mnist_labels[i]) 101 | correct_count += 1; 102 | } 103 | auto elapsed = std::chrono::duration_cast(stop-start).count(); 104 | 105 | std::cout << "[" << fbscrypto::FBSFHEPARAMSET_NAMES[paramset_idx] << "]" << " Accuracy is " << correct_count / n << " " << std::endl; 106 | std::cout << "The evaluation of " << n << " samples took " << elapsed << "seconds" << std::endl << std::endl; 107 | } 108 | 109 | void run_mnist_2_encrypted(std::string path, uint64_t modulus, uint32_t paramset_idx, uint32_t n) { 110 | 111 | std::vector moduli = {modulus}; 112 | 113 | CryptoData data(fbscrypto::FBSFHEPARAMSET_LIST[paramset_idx], moduli); 114 | 115 | if (path[path.size() - 1] != '/') 116 | path.append("/"); 117 | 118 | auto mnist_images_path = path + "dataset/mnist_100_images.csv"; 119 | auto mnist_labels_path = path + "dataset/mnist_100_labels.csv"; 120 | 121 | // import images 122 | auto mnist_images = read_encrypted_mnist100_3d(data, mnist_images_path); 123 | auto mnist_labels = read_mnist100_labels(mnist_labels_path); 124 | 125 | CiphertextNetwork nn(data); 126 | nn.build_from_directory(path); 127 | 128 | auto start = TICK; 129 | 130 | std::vector results(n); 131 | 132 | for(uint32_t ctr = 0; ctr < n; ctr++) { 133 | std::shared_ptr tensor = mnist_images[ctr]; 134 | auto result = nn.run(tensor); 135 | auto now = std::chrono::system_clock::now(); 136 | auto now_time = std::chrono::system_clock::to_time_t(now); 137 | std::cout << "Finished iteration " << ctr << " at " << std::ctime(&now_time) << std::endl; 138 | for(uint32_t i = 0; i < 10; i++) { 139 | auto enc_elem = result->at({i}); 140 | auto elem = data.DecryptCRT(enc_elem); 141 | if (elem.at(0) >= 1ull) { 142 | results[ctr] = i; 143 | break; 144 | } 145 | } 146 | } 147 | 148 | auto stop = TICK; 149 | 150 | double correct_count = 0; 151 | for(uint32_t i = 0; i < n; i++) { 152 | if (results[i] == mnist_labels[i]) 153 | correct_count += 1; 154 | } 155 | auto elapsed = std::chrono::duration_cast(stop-start).count(); 156 | 157 | std::cout << "[" << fbscrypto::FBSFHEPARAMSET_NAMES[paramset_idx] << "]" << " Accuracy is " << correct_count / n << " " << std::endl; 158 | std::cout << "The evaluation of " << n << " samples took " << elapsed << "seconds" << std::endl << std::endl; 159 | } 160 | 161 | void run_mnist_2_plain(std::string path, uint64_t modulus, uint32_t n) { 162 | 163 | std::vector moduli = {modulus}; 164 | 165 | if (path[path.size() - 1] != '/') 166 | path.append("/"); 167 | 168 | auto mnist_images_path = path + "dataset/mnist_100_images.csv"; 169 | auto mnist_labels_path = path + "dataset/mnist_100_labels.csv"; 170 | 171 | // import images 172 | auto mnist_images = read_plain_mnist100_3d(moduli, mnist_images_path); 173 | auto mnist_labels = read_mnist100_labels(mnist_labels_path); 174 | 175 | PlaintextNetwork nn(moduli); 176 | nn.build_from_directory(path); 177 | 178 | auto start = TICK; 179 | 180 | std::vector results; 181 | for(uint32_t j = 0; j < n; j++) { 182 | std::shared_ptr tensor = mnist_images[j]; 183 | auto result = nn.run(tensor); 184 | for(uint32_t i = 0; i < 10; i++) { 185 | auto elem = result->at({i}); 186 | if (elem.at(0) == 1ull) { 187 | results.push_back(i); 188 | break; 189 | } 190 | } 191 | } 192 | 193 | auto stop = TICK; 194 | 195 | double correct_count = 0; 196 | for(uint32_t i = 0; i < n; i++) { 197 | if (results[i] == mnist_labels[i]) 198 | correct_count += 1; 199 | } 200 | auto elapsed = std::chrono::duration_cast(stop-start).count(); 201 | 202 | std::cout << "Plaintext evaluation accuracy is " << correct_count / n << " % " << std::endl; 203 | std::cout << "The evaluation of 100 samples took " << elapsed << " seconds" << std::endl; 204 | } 205 | 206 | 207 | } -------------------------------------------------------------------------------- /NN/src/Tensor.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 07.08.21. 3 | // 4 | 5 | #include "Tensor.h" 6 | 7 | namespace NN { 8 | 9 | void pad_plaintext_tensor3d(std::vector & moduli, const std::shared_ptr& in, uint32_t pad_t, 10 | uint32_t pad_b, uint32_t pad_l, uint32_t pad_r) { 11 | uint32_t shapeX, shapeY, shapeZ; 12 | std::tie(shapeX, shapeY, shapeZ) = in->GetShape(); 13 | 14 | // create nil elements 15 | std::vector padding_top_bottom; 16 | std::vector padding_left, padding_right; 17 | 18 | // top and bottom pad 19 | for(uint32_t j = 0; j < shapeX + pad_l + pad_r; j++) { 20 | padding_top_bottom.emplace_back(0, moduli); 21 | } 22 | 23 | // side padding 24 | for(uint32_t p_i = 0; p_i < pad_l; p_i++) { 25 | padding_left.emplace_back(0, moduli); 26 | } 27 | 28 | for(uint32_t p_i = 0; p_i < pad_r; p_i++) { 29 | padding_right.emplace_back(0, moduli); 30 | } 31 | 32 | PlaintextTensor1D top_tensor(padding_top_bottom); 33 | 34 | for(uint32_t i = 0; i < shapeZ; i++) { 35 | auto& current_channel = (*in)[i]; 36 | 37 | for(uint32_t j = 0; j < shapeY; j++) { 38 | current_channel[j].insert(0, padding_left); 39 | current_channel[j].append(padding_right); 40 | } 41 | 42 | current_channel.GetShapeX() += pad_r + pad_l; 43 | 44 | // TODO: Use the functions I created for that... 45 | for(uint32_t p_i = 0; p_i < pad_t; p_i++) { 46 | current_channel.insert(0, top_tensor); 47 | } 48 | 49 | for(uint32_t p_i = 0; p_i < pad_b; p_i++) { 50 | current_channel.append(top_tensor); 51 | } 52 | } 53 | 54 | in->GetShapeY() += pad_t + pad_b; 55 | in->GetShapeX() += pad_l + pad_r; 56 | in->PropagateShapeChange(); 57 | } 58 | 59 | void pad_ciphertext_tensor3d(CryptoData& data, const std::shared_ptr& in, 60 | uint32_t pad_t, uint32_t pad_b, uint32_t pad_l, uint32_t pad_r) { 61 | 62 | auto moduli = data.GetModuli(); 63 | 64 | uint32_t shapeX, shapeY, shapeZ; 65 | std::tie(shapeX, shapeY, shapeZ) = in->GetShape(); 66 | 67 | // create nil elements 68 | std::vector padding_top_bottom; 69 | std::vector padding_left, padding_right; 70 | 71 | auto zero = data.EncryptCRT(0); 72 | // top and bottom pad 73 | for(uint32_t j = 0; j < shapeX + pad_l + pad_r; j++) { 74 | padding_top_bottom.push_back(zero); 75 | } 76 | 77 | // side padding 78 | for(uint32_t p_i = 0; p_i < pad_l; p_i++) { 79 | padding_left.push_back(zero); 80 | } 81 | 82 | for(uint32_t p_i = 0; p_i < pad_r; p_i++) { 83 | padding_right.push_back(zero); 84 | } 85 | 86 | CiphertextTensor1D top_tensor(padding_top_bottom); 87 | 88 | for(uint32_t i = 0; i < shapeZ; i++) { 89 | auto& current_channel = (*in)[i]; 90 | 91 | for(uint32_t j = 0; j < shapeY; j++) { 92 | current_channel[j].insert(0, padding_left); 93 | current_channel[j].append(padding_right); 94 | } 95 | 96 | current_channel.GetShapeX() += pad_r + pad_l; 97 | 98 | // TODO: Use the functions I created for that... 99 | for(uint32_t p_i = 0; p_i < pad_t; p_i++) { 100 | current_channel.insert(0, top_tensor); 101 | } 102 | 103 | for(uint32_t p_i = 0; p_i < pad_b; p_i++) { 104 | current_channel.append(top_tensor); 105 | } 106 | } 107 | 108 | in->GetShapeY() += pad_t + pad_b; 109 | in->GetShapeX() += pad_l + pad_r; 110 | in->PropagateShapeChange(); 111 | } 112 | } -------------------------------------------------------------------------------- /NN/src/Utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #include "Utils.h" 6 | 7 | namespace NN { 8 | 9 | fbscrypto::LWECiphertext CryptoData::EncryptNormal(uint64_t value, uint64_t modulus) { 10 | auto encoded = ctx.Encode(value, modulus, fbscrypto::FRESH); 11 | return ctx.Encrypt(this->key, encoded); 12 | } 13 | 14 | CiphertextCRT CryptoData::EncryptCRT(int64_t value, fbscrypto::CIPHERTEXT_STATE from) { 15 | std::vector temp; 16 | 17 | for(unsigned long i : moduli) { 18 | int64_t tmp = value % int64_t(i); 19 | auto encoded = ctx.Encode(tmp, i, from); 20 | temp.push_back(ctx.Encrypt(key, encoded, from)); 21 | } 22 | 23 | return {temp, this->moduli}; 24 | } 25 | 26 | CiphertextCRT CryptoData::EncryptCRT(std::vector &values, fbscrypto::CIPHERTEXT_STATE from) { 27 | std::vector temp; 28 | 29 | for(uint32_t i = 0; i < values.size(); i++) { 30 | 31 | uint64_t mod = moduli[i]; 32 | int64_t val = values[i]; 33 | 34 | auto encoded = ctx.Encode(val, mod, from); 35 | temp.push_back(ctx.Encrypt(key, encoded, from)); 36 | } 37 | 38 | return {temp, this->moduli}; 39 | } 40 | 41 | CiphertextCRT CryptoData::DoKeySwitch(const CiphertextCRT &crt) { 42 | std::vector cts; 43 | for(uint32_t i = 0; i < moduli.size(); i++) { 44 | auto& elem = crt.at(i); 45 | cts.push_back(ctx.Finalize(elem, fbscrypto::SKIP_STEP::KEYSWITCH)); 46 | } 47 | 48 | return {cts, moduli}; 49 | } 50 | 51 | CiphertextCRT CryptoData::DoModswitch(const CiphertextCRT &crt) { 52 | std::vector cts; 53 | for(uint32_t i = 0; i < moduli.size(); i++) { 54 | auto& elem = crt.at(i); 55 | cts.push_back(ctx.Finalize(elem, fbscrypto::SKIP_STEP::MODSWITCH)); 56 | } 57 | 58 | return {cts, moduli}; 59 | } 60 | 61 | PlaintextCRT CryptoData::DecryptCRT(const CiphertextCRT &value, fbscrypto::CIPHERTEXT_STATE from) { 62 | 63 | std::vector values; 64 | int64_t pt; 65 | for(uint32_t i = 0; i < moduli.size(); i++) { 66 | auto ct = value.at(i); 67 | ctx.Decrypt(this->key, ct, &pt); 68 | values.push_back(ctx.Decode(pt, moduli[i], from)); 69 | } 70 | 71 | return {values, this->moduli}; 72 | } 73 | 74 | const vector &CryptoData::GetModuli() const { 75 | return moduli; 76 | } 77 | 78 | CiphertextCRT CryptoData::BootstrapCRT(const CiphertextCRT &input, std::vector& functions, 79 | fbscrypto::SKIP_STEP step) { 80 | 81 | if (moduli.size() != functions.size()) { 82 | throw std::invalid_argument("Number of functions does not match number of moduli/ciphertext components"); 83 | } 84 | 85 | std::vector results; 86 | for(uint32_t i = 0; i < moduli.size(); i++) { 87 | results.push_back(ctx.FullDomainBootstrap(input.at(i), functions.at(i), step)); 88 | } 89 | 90 | return {results, this->moduli}; 91 | } 92 | 93 | void read_signed_matrix_from_csv(int64_t* buffer, uint32_t shapeX, uint32_t shapeY, std::string& path) { 94 | 95 | 96 | std::string line, field; 97 | std::ifstream iF; 98 | 99 | iF.open(path); 100 | uint32_t i = 0, j = 0; 101 | while (std::getline(iF, line) && (i < shapeY) ) { 102 | 103 | std::istringstream s(line); 104 | 105 | while (std::getline(s, field, ',') && (j < shapeX)) { 106 | 107 | int64_t value = std::stoll(field); 108 | buffer[i * shapeX + j] = value; 109 | 110 | j++; 111 | } 112 | j = 0; 113 | i++; 114 | } 115 | 116 | iF.close(); 117 | 118 | } 119 | 120 | void read_unsigned_matrix_from_csv(uint64_t* buffer, uint32_t shapeX, uint32_t shapeY, std::string& path) { 121 | 122 | std::string line, field; 123 | std::ifstream iF; 124 | 125 | iF.open(path); 126 | uint32_t i = 0, j = 0; 127 | while (std::getline(iF, line) && (i < shapeY)) { 128 | 129 | std::istringstream s(line); 130 | 131 | while (std::getline(s, field, ',') && (j < shapeX)) { 132 | 133 | uint64_t value = std::stoull(field); 134 | buffer[i * shapeX + j] = value; 135 | 136 | j++; 137 | } 138 | j = 0; 139 | i++; 140 | } 141 | 142 | iF.close(); 143 | 144 | } 145 | 146 | void read_signed_vector_from_csv(int64_t* buffer, uint32_t shapeY, std::string& path) { 147 | 148 | std::string line, field; 149 | std::ifstream iF; 150 | 151 | iF.open(path); 152 | uint32_t i = 0; 153 | while (std::getline(iF, line) && (i < shapeY)) { 154 | uint64_t value = std::stoll(line); 155 | buffer[i++] = value; 156 | } 157 | 158 | iF.close(); 159 | 160 | } 161 | 162 | void read_unsigned_vector_from_csv(uint64_t* buffer, uint32_t shapeY, std::string& path) { 163 | 164 | 165 | std::string line, field; 166 | std::ifstream iF; 167 | 168 | iF.open(path); 169 | uint32_t i = 0; 170 | while (std::getline(iF, line) && (i < shapeY)) { 171 | uint64_t value = std::stoull(line); 172 | buffer[i++] = value; 173 | } 174 | 175 | iF.close(); 176 | 177 | } 178 | 179 | long double evaluate_horner(long double input, const long double* coefs, uint64_t size) { 180 | 181 | if (size == 0) 182 | return 0.; 183 | 184 | auto accu = coefs[size - 1]; 185 | for(uint32_t i = 1; i < size; i++) { 186 | accu = (coefs[size - i - 1] + accu * input); 187 | } 188 | 189 | return accu; 190 | } 191 | 192 | 193 | 194 | } -------------------------------------------------------------------------------- /NN/src/layers/Activation.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 08.08.21. 3 | // 4 | 5 | #include "layers/Activation.h" 6 | #include "ActivationFunctions.h" 7 | 8 | #include 9 | 10 | namespace NN { 11 | 12 | namespace Layers { 13 | 14 | PlaintextActivation::PlaintextActivation(std::vector &moduli, uint64_t scale, uint64_t scale_inv, ACTIVATION activation, bool do_scale) 15 | : PlaintextLayer(moduli, scale, scale_inv), activation(activation) { 16 | if(this->moduli.size() > 1 && (activation != ACTIVATION::TANH_POLY) 17 | && (activation != ACTIVATION::SIGMOID_POLY) && (activation != ACTIVATION::RELU_POLY) 18 | && (activation != ACTIVATION::NONE)) { 19 | std::cerr << "Using activation functions that are not approximated by polynomials for |moduli| > 1 is" 20 | "can go wrong. Proceed with caution..." << std::endl; 21 | } 22 | 23 | switch (activation) { 24 | case ACTIVATION::RELU: { 25 | F = [this](uint64_t v, uint64_t q) ->uint64_t { 26 | 27 | if (v > (q / 2)) return 0ull; 28 | 29 | long double dv = v; 30 | auto quot = dv / double(this->scale); 31 | long double frac; 32 | if (std::modf(quot, &frac) > 0.5) 33 | return uint64_t(std::ceil(quot)); 34 | else 35 | return uint64_t(std::floor(quot)); 36 | //return (v > (q / 2)) ? 0 : std::round(dv / double(this->scale)); 37 | }; 38 | break; 39 | }; 40 | case ACTIVATION::RELU_POLY: { 41 | F = [](uint64_t v, uint64_t q) { 42 | return RELU_POLY(v); 43 | }; 44 | break; 45 | } 46 | case ACTIVATION::TANH: { 47 | F = [](uint64_t v, uint64_t q) { 48 | return TANH(v); 49 | }; 50 | break; 51 | } 52 | case ACTIVATION::TANH_POLY: { 53 | F = [](uint64_t v, uint64_t q) { 54 | return TANH_POLY(v); 55 | }; 56 | break; 57 | } 58 | case ACTIVATION::SIGMOID: { 59 | F = [](uint64_t v, uint64_t q) { 60 | return TANH(v); 61 | }; 62 | break; 63 | } 64 | case ACTIVATION::SIGMOID_POLY: { 65 | F = [](uint64_t v, uint64_t q) { 66 | return TANH(v); 67 | }; 68 | break; 69 | } 70 | default: { 71 | F = [](uint64_t v, uint64_t q) {return v; }; 72 | } 73 | } 74 | 75 | } 76 | 77 | void PlaintextActivation::build_from_path(std::vector &paths) { 78 | throw std::runtime_error("No need to build Activation layer."); 79 | } 80 | 81 | void PlaintextActivation::compute_activation_1d(std::shared_ptr& tensor1D) { 82 | 83 | auto shape = tensor1D->GetShape(); 84 | 85 | if ((shape == 0) || (this->moduli.empty())) 86 | return; 87 | 88 | if(activation == ACTIVATION::SOFTMAX) { 89 | // for the softmax in the final layer 90 | // we compute in "plaintext", so we have to do it within the linear layer part 91 | return; 92 | } 93 | 94 | else { 95 | for(uint32_t i = 0; i < shape; i++) { 96 | auto& elem = (*tensor1D)[i].GetContents(); 97 | for(uint32_t j = 0; j < this->moduli.size(); j++) { 98 | elem[j] = F(elem[j], this->moduli[j]); 99 | } 100 | } 101 | } 102 | } 103 | 104 | void PlaintextActivation::compute_activation_2d(std::shared_ptr& tensor2D) { 105 | auto shape = tensor2D->GetShape(); 106 | if ((shape.first == 0) || (shape.second == 0) || (this->moduli.empty())) 107 | return; 108 | 109 | for(uint32_t i = 0; i < shape.second; i++) { 110 | for(uint32_t j = 0; j < shape.first; j++) { 111 | auto& coefs = (*tensor2D)[i][j].GetContents(); 112 | for(uint32_t k = 0; k < this->moduli.size(); k++) { 113 | coefs[k] = F(coefs[k], this->moduli[k]); 114 | } 115 | } 116 | } 117 | } 118 | 119 | void PlaintextActivation::compute_activation_3d(std::shared_ptr& tensor3D) { 120 | uint32_t shapeX, shapeY, shapeZ; 121 | std::tie(shapeX,shapeY,shapeZ) = tensor3D->GetShape(); 122 | for(uint32_t h = 0; h < shapeZ; h++) { 123 | for(uint32_t i = 0; i < shapeY; i++) { 124 | for(uint32_t j = 0; j < shapeX; j++) { 125 | auto& coefs = (*tensor3D)[h][i][j].GetContents(); 126 | for(uint32_t k = 0; k < this->moduli.size(); k++) { 127 | coefs[k] = F(coefs[k], this->moduli[k]); 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | std::shared_ptr PlaintextActivation::forward(std::shared_ptr &tensor) { 135 | 136 | std::shared_ptr tensor1D; 137 | std::shared_ptr tensor2D; 138 | std::shared_ptr tensor3D; 139 | 140 | if (activation != ACTIVATION::NONE) { 141 | if((tensor1D = std::dynamic_pointer_cast(tensor)) != nullptr) { 142 | compute_activation_1d(tensor1D); 143 | return tensor1D; 144 | } 145 | 146 | if ((tensor2D = std::dynamic_pointer_cast(tensor)) != nullptr) { 147 | compute_activation_2d(tensor2D); 148 | return tensor2D; 149 | } 150 | 151 | if ((tensor3D = std::dynamic_pointer_cast(tensor)) != nullptr) { 152 | compute_activation_3d(tensor3D); 153 | return tensor3D; 154 | } 155 | } 156 | 157 | return tensor; 158 | } 159 | 160 | // Ciphertext 161 | 162 | CiphertextActivation::CiphertextActivation(CryptoData& data, uint64_t scale, uint64_t scale_inv, ACTIVATION activation, bool do_scale) 163 | : CiphertextLayer(data.GetModuli(), scale, scale_inv), data(data), activation(activation) { 164 | 165 | 166 | if(this->moduli.size() > 1 && (activation != ACTIVATION::TANH_POLY) 167 | && (activation != ACTIVATION::SIGMOID_POLY) && (activation != ACTIVATION::RELU_POLY) 168 | && (activation != ACTIVATION::NONE)) { 169 | std::cerr << "Using activation functions that are not approximated by polynomials for |moduli| > 1 is" 170 | "can go wrong. Proceed with caution..." << std::endl; 171 | } 172 | 173 | std::function F; 174 | switch (activation) { 175 | case ACTIVATION::NONE: { 176 | F = [](uint64_t v, uint64_t q) { 177 | return v; 178 | }; 179 | break; 180 | } 181 | case ACTIVATION::RELU: { 182 | F = [this](uint64_t v, uint64_t q) ->uint64_t { 183 | 184 | if (v >= (q / 2)) return 0ull; 185 | 186 | long double dv = v; 187 | auto quot = dv / double(this->scale); 188 | long double frac; 189 | if (std::modf(quot, &frac) > 0.5) 190 | return uint64_t(std::ceil(quot)); 191 | else 192 | return uint64_t(std::floor(quot)); 193 | //return (v > (q / 2)) ? 0 : std::round(dv / double(this->scale)); 194 | }; 195 | break; 196 | }; 197 | case ACTIVATION::RELU_POLY: { 198 | F = [](uint64_t v, uint64_t q) { 199 | return RELU_POLY(v); 200 | }; 201 | break; 202 | } 203 | case ACTIVATION::TANH: { 204 | F = [](uint64_t v, uint64_t q) { 205 | return TANH(v); 206 | }; 207 | break; 208 | } 209 | case ACTIVATION::TANH_POLY: { 210 | F = [](uint64_t v, uint64_t q) { 211 | return TANH_POLY(v); 212 | }; 213 | break; 214 | } 215 | case ACTIVATION::SIGMOID: { 216 | F = [](uint64_t v, uint64_t q) { 217 | return TANH(v); 218 | }; 219 | break; 220 | } 221 | case ACTIVATION::SIGMOID_POLY: { 222 | F = [](uint64_t v, uint64_t q) { 223 | return TANH(v); 224 | }; 225 | break; 226 | } 227 | default: { 228 | F = [](uint64_t v, uint64_t q) {return v; }; 229 | } 230 | } 231 | 232 | for(auto& modulus : this->moduli) 233 | EF.emplace_back([F, modulus](uint64_t v) {return (F(v, modulus)) % modulus;}, modulus); 234 | } 235 | 236 | void CiphertextActivation::build_from_path(std::vector &paths) { 237 | throw std::runtime_error("No need to build anything for activation layer"); 238 | } 239 | 240 | void CiphertextActivation::compute_activation_1d(shared_ptr &tensor1D) { 241 | 242 | auto shape = tensor1D->GetShape(); 243 | 244 | if ((shape == 0) || (this->moduli.empty())) 245 | return; 246 | 247 | #pragma omp parallel for 248 | for(uint32_t i = 0; i < shape; i++) { 249 | CiphertextCRT tmp = data.DoKeySwitch((*tensor1D)[i]); 250 | auto dec = data.DecryptCRT(tmp, fbscrypto::TRIVIAL); 251 | (*tensor1D)[i] = data.BootstrapCRT(tmp, EF, fbscrypto::SKIP_STEP::KEYSWITCH); 252 | } 253 | 254 | } 255 | 256 | void CiphertextActivation::compute_activation_2d(shared_ptr &tensor2D) { 257 | 258 | auto shape = tensor2D->GetShape(); 259 | if ((shape.first == 0) || (shape.second == 0) || (this->moduli.empty())) 260 | return; 261 | 262 | #pragma omp parallel for collapse(2) 263 | for(uint32_t i = 0; i < shape.second; i++) { 264 | for(uint32_t j = 0; j < shape.first; j++) { 265 | (*tensor2D)[i][j] = data.BootstrapCRT((*tensor2D)[i][j], EF); 266 | } 267 | } 268 | } 269 | 270 | void CiphertextActivation::compute_activation_3d(shared_ptr &tensor3D) { 271 | uint32_t shapeX, shapeY, shapeZ; 272 | std::tie(shapeX,shapeY,shapeZ) = tensor3D->GetShape(); 273 | 274 | #pragma omp parallel for collapse(3) 275 | for(uint32_t h = 0; h < shapeZ; h++) { 276 | for(uint32_t i = 0; i < shapeY; i++) { 277 | for(uint32_t j = 0; j < shapeX; j++) { 278 | auto tmp = data.DoKeySwitch((*tensor3D)[h][i][j]); 279 | (*tensor3D)[h][i][j] = data.BootstrapCRT(tmp, EF, fbscrypto::SKIP_STEP::KEYSWITCH); 280 | } 281 | } 282 | } 283 | } 284 | 285 | std::shared_ptr CiphertextActivation::forward(std::shared_ptr &tensor) { 286 | std::shared_ptr tensor1D; 287 | std::shared_ptr tensor2D; 288 | std::shared_ptr tensor3D; 289 | 290 | if((tensor1D = std::dynamic_pointer_cast(tensor)) != nullptr) { 291 | compute_activation_1d(tensor1D); 292 | return tensor1D; 293 | } 294 | 295 | if ((tensor2D = std::dynamic_pointer_cast(tensor)) != nullptr) { 296 | compute_activation_2d(tensor2D); 297 | return tensor2D; 298 | } 299 | 300 | if ((tensor3D = std::dynamic_pointer_cast(tensor)) != nullptr) { 301 | compute_activation_3d(tensor3D); 302 | return tensor3D; 303 | } 304 | 305 | return tensor; 306 | } 307 | } 308 | } -------------------------------------------------------------------------------- /NN/src/layers/AveragePool2D.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 07.08.21. 3 | // 4 | 5 | #include 6 | 7 | #include "layers/AveragePool2D.h" 8 | 9 | namespace NN { 10 | namespace Layers { 11 | 12 | inline bool is_outside_box(uint32_t i, uint32_t j, uint32_t tl_i, uint32_t tl_j, uint32_t br_i, uint32_t br_j) { 13 | return (i < tl_i) || (i >= br_i) || (j < tl_j) || (j >= br_j); 14 | } 15 | 16 | PlaintextAveragePool2D::PlaintextAveragePool2D(std::vector &moduli, uint64_t scale, uint64_t scale_inv, 17 | std::pair& pool_size, 18 | std::pair& stride, PADDING pad) 19 | : PlaintextLayer(moduli, scale, scale_inv), pool_size(std::move(pool_size)), stride(stride), pad(pad) { 20 | 21 | } 22 | 23 | void PlaintextAveragePool2D::build_from_path(std::vector &paths) { 24 | throw std::runtime_error("There is no need to read any data for an average pool."); 25 | } 26 | 27 | std::shared_ptr PlaintextAveragePool2D::forward(std::shared_ptr &input) { 28 | 29 | const std::shared_ptr tensor3D = std::dynamic_pointer_cast(input); 30 | 31 | uint32_t shapeX, shapeY, shapeZ; 32 | uint32_t output_y, output_x, padding_height, padding_width; 33 | uint32_t pad_t, pad_b, pad_l, pad_r; 34 | 35 | std::tie(shapeX, shapeY, shapeZ) = tensor3D->GetShape(); 36 | 37 | // compute output parameters and padding 38 | if (this->pad == SAME) { 39 | output_y = std::ceil(double(shapeY) / this->stride.second); 40 | output_x = std::ceil(double(shapeX) / this->stride.first); 41 | } else { 42 | output_y = std::ceil(double(shapeY - pool_size.second + 1) / this->stride.second); 43 | output_x = std::ceil(double(shapeX - pool_size.first + 1) / this->stride.first); 44 | } 45 | 46 | if (shapeY % stride.second == 0) { 47 | padding_height = pool_size.second > stride.second ? pool_size.second - stride.second : 0; 48 | } else { 49 | padding_height = pool_size.second > (stride.second % shapeY) ? pool_size.second - (stride.second % shapeY) : 0; 50 | } 51 | 52 | if (shapeX % stride.first == 0) { 53 | padding_width = pool_size.first > stride.first ? pool_size.first - stride.first : 0; 54 | } else { 55 | padding_width = pool_size.first > (stride.first % shapeX) ? pool_size.first - (stride.first % shapeX) : 0; 56 | } 57 | 58 | pad_t = padding_height / 2; 59 | pad_b = padding_height - pad_t; 60 | 61 | pad_l = padding_width / 2; 62 | pad_r = padding_width - pad_l; 63 | 64 | if (pad == SAME) 65 | pad_plaintext_tensor3d(this->moduli, tensor3D, pad_t, pad_b, pad_l, pad_r); 66 | 67 | double garbage; 68 | std::vector>> output(shapeZ); 69 | for(uint32_t c_i = 0; c_i < shapeZ; c_i++) { 70 | std::vector> output_channel(output_y); 71 | auto& input_channel = (*tensor3D)[c_i]; 72 | for(uint32_t o_j = 0; o_j < output_y; o_j++) { 73 | std::vector row; 74 | for(uint32_t o_i = 0; o_i < output_x; o_i++) { 75 | row.emplace_back(0, this->moduli); 76 | uint32_t div_coef = pool_size.first * pool_size.second; 77 | 78 | uint32_t in_i = o_i * stride.first; 79 | uint32_t in_j = o_j * stride.second; 80 | 81 | for(uint32_t p_i = 0; p_i < pool_size.first; p_i++) { 82 | for(uint32_t p_j = 0; p_j < pool_size.second; p_j++) { 83 | uint32_t read_i = in_i + p_i; 84 | uint32_t read_j = in_j + p_j; 85 | 86 | if (is_outside_box(read_i, read_j, pad_l, pad_t, pad_l + shapeX, pad_t + shapeY)) { 87 | div_coef--; 88 | continue; 89 | } 90 | 91 | row[o_i] += input_channel[read_j][read_i]; 92 | } 93 | } 94 | if (scale_inverse == 0) { 95 | auto contents = row[o_i].GetContents(); 96 | for(uint32_t i = 0; i < this->moduli.size(); i++) { 97 | double tmp = contents[i]; 98 | if (tmp >= (this->moduli[i] / 2)) 99 | tmp -= this->moduli[i]; 100 | 101 | tmp /= div_coef; 102 | auto coef = std::roundl((std::modf(tmp, &garbage)) > 0.5 ? std::ceil(tmp) : std::floor(tmp)); 103 | if (coef < 0) 104 | coef += this->moduli[i]; 105 | contents[i] = uint64_t(coef); 106 | } 107 | for(auto& coef : row[o_i].GetContents()) { 108 | 109 | double tmp = double(coef) / div_coef; 110 | coef = std::roundl((std::modf(tmp, &garbage)) > 0.5 ? std::ceil(tmp) : std::floor(tmp)); 111 | } 112 | } else { 113 | auto& contents = row[o_i].GetContents(); 114 | for(uint32_t i = 0; i < this->moduli.size(); i++) { 115 | 116 | uint64_t tmp = (contents[i] * scale_inverse ) % this->moduli[i]; 117 | int64_t tmp_s; 118 | 119 | if (tmp >= (this->moduli[i] / 2)) 120 | tmp_s = int64_t(tmp) - int64_t(this->moduli[i]); 121 | else 122 | tmp_s = int64_t(tmp); 123 | 124 | long double res = (long double)(tmp_s) / div_coef; 125 | 126 | auto tmp_d = std::roundl((std::modf(res, &garbage)) > 0.5 ? std::ceil(res) : std::floor(res)); 127 | if (tmp_d < 0) 128 | tmp_d += this->moduli[i]; 129 | contents[i] = (uint64_t(tmp_d) * scale) % this->moduli[i]; 130 | } 131 | } 132 | } 133 | output_channel[o_j] = std::move(row); 134 | } 135 | output[c_i] = std::move(output_channel); 136 | } 137 | 138 | return std::make_shared(output); 139 | } 140 | 141 | CiphertextAveragePool2D::CiphertextAveragePool2D(CryptoData &data, uint64_t scale, uint64_t scale_inv, 142 | std::pair pool_size, 143 | std::pair stride, PADDING pad) 144 | : CiphertextLayer(data.GetModuli(), scale, scale_inv), 145 | data(data), pool_size(pool_size), stride(stride), pad(pad){ 146 | 147 | } 148 | 149 | void CiphertextAveragePool2D::build_from_path(std::vector &paths) { 150 | 151 | } 152 | 153 | std::shared_ptr CiphertextAveragePool2D::forward(std::shared_ptr &input) { 154 | const std::shared_ptr tensor3D = std::dynamic_pointer_cast(input); 155 | 156 | uint32_t shapeX, shapeY, shapeZ; 157 | uint32_t output_y, output_x, padding_height, padding_width; 158 | uint32_t pad_t, pad_b, pad_l, pad_r; 159 | 160 | std::tie(shapeX, shapeY, shapeZ) = tensor3D->GetShape(); 161 | 162 | // compute output parameters and padding 163 | if (this->pad == SAME) { 164 | output_y = std::ceil(double(shapeY) / this->stride.second); 165 | output_x = std::ceil(double(shapeX) / this->stride.first); 166 | } else { 167 | output_y = std::ceil(double(shapeY - pool_size.second + 1) / this->stride.second); 168 | output_x = std::ceil(double(shapeX - pool_size.first + 1) / this->stride.first); 169 | } 170 | 171 | if (shapeY % stride.second == 0) { 172 | padding_height = pool_size.second > stride.second ? pool_size.second - stride.second : 0; 173 | } else { 174 | padding_height = pool_size.second > (stride.second % shapeY) ? pool_size.second - (stride.second % shapeY) : 0; 175 | } 176 | 177 | if (shapeX % stride.first == 0) { 178 | padding_width = pool_size.first > stride.first ? pool_size.first - stride.first : 0; 179 | } else { 180 | padding_width = pool_size.first > (stride.first % shapeX) ? pool_size.first - (stride.first % shapeX) : 0; 181 | } 182 | 183 | pad_t = padding_height / 2; 184 | pad_b = padding_height - pad_t; 185 | 186 | pad_l = padding_width / 2; 187 | pad_r = padding_width - pad_l; 188 | 189 | if (pad == SAME) 190 | pad_ciphertext_tensor3d(this->data, tensor3D, pad_t, pad_b, pad_l, pad_r); 191 | 192 | double garbage; 193 | std::vector>> output(shapeZ); 194 | #pragma omp parallel for 195 | for(uint32_t c_i = 0; c_i < shapeZ; c_i++) { 196 | std::vector> output_channel(output_y); 197 | auto& input_channel = (*tensor3D)[c_i]; 198 | for(uint32_t o_j = 0; o_j < output_y; o_j++) { 199 | std::vector row; 200 | for(uint32_t o_i = 0; o_i < output_x; o_i++) { 201 | row.emplace_back(data.EncryptCRT(0)); 202 | uint32_t div_coef = pool_size.first * pool_size.second; 203 | 204 | uint32_t in_i = o_i * stride.first; 205 | uint32_t in_j = o_j * stride.second; 206 | 207 | for(uint32_t p_i = 0; p_i < pool_size.first; p_i++) { 208 | for(uint32_t p_j = 0; p_j < pool_size.second; p_j++) { 209 | uint32_t read_i = in_i + p_i; 210 | uint32_t read_j = in_j + p_j; 211 | 212 | if (is_outside_box(read_i, read_j, pad_l, pad_t, pad_l + shapeX, pad_t + shapeY)) { 213 | div_coef--; 214 | continue; 215 | } 216 | 217 | row[o_i] += input_channel[read_j][read_i]; 218 | } 219 | } 220 | std::vector bstF; 221 | for(auto& modulus: this->moduli) { 222 | bstF.emplace_back([modulus, div_coef](uint64_t a) { 223 | int64_t m = a > (modulus / 2) ? int64_t(a) - int64_t(modulus) : int64_t(a); 224 | double r = double(m) / div_coef; 225 | if (r < 0) { 226 | return uint64_t(std::round(r + int64_t(modulus))); 227 | } else { 228 | return uint64_t(std::round(r)); 229 | } 230 | }, modulus); 231 | } 232 | auto tmp = data.DoKeySwitch(row[o_i]); 233 | row[o_i] = data.BootstrapCRT(tmp, bstF, fbscrypto::SKIP_STEP::KEYSWITCH); 234 | } 235 | output_channel[o_j] = std::move(row); 236 | } 237 | output[c_i] = std::move(output_channel); 238 | } 239 | 240 | return std::make_shared(output); 241 | 242 | } 243 | 244 | // TODO Ciperhtext equivalent 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /NN/src/layers/Linear.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 27.07.21. 3 | // 4 | 5 | #include "layers/Linear.h" 6 | 7 | namespace NN { 8 | 9 | std::shared_ptr Layers::PlaintextLinear::forward(std::shared_ptr &tens) { 10 | 11 | std::shared_ptr tensor1D = std::dynamic_pointer_cast(tens); 12 | auto shape = tensor1D->GetShape(); 13 | 14 | std::vector result(shapeX); 15 | if (this->activation != ACTIVATION::SOFTMAX) { 16 | #pragma omp parallel for 17 | for(uint32_t i = 0; i < shapeX; i++) { 18 | auto accu = PlaintextCRT(B_v[i] % this->moduli[0], this->moduli); 19 | for(uint32_t j = 0; j < shapeY; j++) { 20 | accu += (*tensor1D)[j] * this->W_m[j][i]; 21 | } 22 | result[i] = std::move(accu); 23 | } 24 | } else { 25 | // assume 1 modulus, otherwise softmax doesn't make sense... 26 | std::vector tmp; 27 | for(uint32_t i = 0; i < shapeX; i++) { 28 | auto accu = (long double)(B_v[i]); 29 | for(uint32_t j = 0; j < shapeY; j++) { 30 | accu += (long double)((*tensor1D)[j].at(0)) * this->W_m[j][i]; 31 | } 32 | tmp.push_back(accu); 33 | } 34 | 35 | auto max_idx = std::distance(tmp.begin(), std::max_element(tmp.begin(), tmp.end())); 36 | for(uint32_t i = 0; i < shapeX; i++) { 37 | if (i == max_idx) 38 | result[i] = PlaintextCRT(1, this->moduli); 39 | else 40 | result[i] = PlaintextCRT(0, this->moduli); 41 | } 42 | } 43 | 44 | std::shared_ptr tmp = std::make_shared(result); 45 | 46 | //return tmp; 47 | return PlaintextActivation::forward(tmp); 48 | } 49 | 50 | void Layers::PlaintextLinear::build_from_path(std::vector &paths) { 51 | 52 | 53 | if (paths.size() < 2) { 54 | throw std::invalid_argument("Linear layer expects two paths. One for W, one for B"); 55 | } 56 | 57 | auto W = new int64_t[shapeX * shapeY]; 58 | 59 | read_signed_matrix_from_csv(W, shapeX, shapeY, paths[0]); 60 | read_signed_vector_from_csv(B_v.data(), shapeX, paths[1]); 61 | 62 | for(uint32_t i = 0; i < shapeX; i++) { 63 | for(uint32_t j = 0; j < shapeY; j++) { 64 | W_m[j][i] = W[j * shapeX + i]; 65 | } 66 | } 67 | 68 | delete[] W; 69 | } 70 | 71 | std::shared_ptr Layers::CiphertextLinear::forward(std::shared_ptr &tens) { 72 | 73 | std::shared_ptr tensor1D = std::dynamic_pointer_cast(tens); 74 | 75 | std::vector accu(shapeX); 76 | std::vector result(shapeX); 77 | 78 | #pragma omp parallel for 79 | for(uint32_t i = 0; i < shapeX; i++) { 80 | auto accu_ct = CryptData.EncryptCRT(B_v[i] % moduli[0], fbscrypto::CIPHERTEXT_STATE::TRIVIAL_BEFORE_KEYSWITCH); 81 | for(uint32_t j = 0; j < shapeY; j++) { 82 | auto tmp = (*tensor1D)[j] * this->W_m[j][i]; 83 | accu_ct += tmp; 84 | } 85 | accu[i] = std::move(accu_ct); 86 | } 87 | 88 | if (activation == ACTIVATION::SOFTMAX) { 89 | std::vector tmp; 90 | auto shape = tensor1D->GetShape(); 91 | std::vector decrypted_tensor(shape); 92 | auto modulus2 = CryptData.GetModuli()[0] >> 1; 93 | 94 | #pragma omp parallel for 95 | for(uint32_t i = 0; i < tensor1D->GetShape(); i++) { 96 | auto tmp1 = CryptData.DoKeySwitch(accu[i]); 97 | decrypted_tensor[i] = std::move(CryptData.DecryptCRT(tmp1, fbscrypto::TRIVIAL)); 98 | } 99 | 100 | for(auto& PT : decrypted_tensor) { 101 | int64_t value = PT.GetContents()[0]; 102 | if (value >= modulus2) { 103 | value -= modulus2; 104 | } 105 | tmp.push_back(value); 106 | } 107 | auto max_idx = std::distance(tmp.begin(), std::max_element(tmp.begin(), tmp.end())); 108 | 109 | // for our classification purpose we do not need the full probability distribution 110 | // of a softmax output. As softmax preserves the ordering, we output the maximum. 111 | for(uint32_t i = 0; i < shapeX; i++) { 112 | if (i == max_idx) 113 | result[i] = std::move(CryptData.EncryptCRT(1)); 114 | else 115 | result[i] = std::move(CryptData.EncryptCRT(0)); 116 | } 117 | return std::make_shared(result); 118 | } 119 | 120 | std::shared_ptr tensor_out = std::make_shared(accu); 121 | return CiphertextActivation::forward(tensor_out); 122 | 123 | } 124 | 125 | void Layers::CiphertextLinear::build_from_path(std::vector &paths) { 126 | 127 | if (paths.size() < 2) { 128 | throw std::invalid_argument("Linear layer expects two paths. One for W, one for B"); 129 | } 130 | 131 | auto W = new int64_t[shapeX * shapeY]; 132 | 133 | read_signed_matrix_from_csv(W, shapeX, shapeY, paths[0]); 134 | read_signed_vector_from_csv(B_v.data(), shapeX, paths[1]); 135 | 136 | for(uint32_t i = 0; i < shapeX; i++) { 137 | for(uint32_t j = 0; j < shapeY; j++) { 138 | W_m[j][i] = W[j * shapeX + i]; 139 | } 140 | }; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Functional Bootstrap 2 | 3 | This is the implementation of the methods described in our paper 4 | "FDFB: Full Domain Functional Bootstrapping Towards Practical Fully Homomorphic Encryption" 5 | 6 | It uses PALISADE (included as a submodule). Furthermore, the functional bootstrap 7 | implementation is a heavily modified version of the *binfhe* module in Palisade together 8 | with a number of additional routines. Note that to use the parameters described 9 | in the paper, one needs to modify the Barrett Reduction methods of PALISADE in order 10 | to support larger moduli. Concretely, the ```ComputeMu``` and ```Mod(Eq)``` methods in ```src/core/include/math/bigintnat/ubintnat.h``` 11 | need to be adapted. One may use ```clangs``` ```_ExtInt``` and a patch will be provided in the future. 12 | 13 | A brief documentation of the methods in included in the header files. 14 | 15 | # Build instructions 16 | 17 | The compiler should be adapted via ```-DCMAKE_CXX_COMPILER``` as there are significant timing differences 18 | between ```gcc``` and ```clang``` 19 | 20 | - ```git clone https://github.com/cispa/full-domain-function-bootstrap``` 21 | - ```git submodule update --init --recursive ``` 22 | - ```mkdir install``` 23 | - ```mkdir build && cd build``` 24 | - ```cmake ..``` 25 | - ```make -j 16``` (Will cause an error, this is normal. The PALISADE build process autogenerates files we need) 26 | - ```make``` 27 | - ```./FBSTest``` or ```./NN``` 28 | -------------------------------------------------------------------------------- /fbs/include/definitions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 06.05.21. 3 | // 4 | 5 | #ifndef FBS_DEFINITIONS_H 6 | #define FBS_DEFINITIONS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* Macros to measure time, for benchmarking */ 14 | #ifdef MEASURE_TIME 15 | 16 | #define TIME_SECTION_MILLIS(Y, X) \ 17 | auto start = std::chrono::high_resolution_clock::now(); \ 18 | X; \ 19 | auto stop = std::chrono::high_resolution_clock::now(); \ 20 | auto elapsed = std::chrono::duration_cast(stop-start); \ 21 | std::cerr << "Section " #Y << " took " << elapsed.count() << "ms." << std::endl; 22 | 23 | 24 | #define TIME_SECTION_MICRO(Y, X) {\ 25 | auto start = std::chrono::high_resolution_clock::now(); \ 26 | X \ 27 | auto stop = std::chrono::high_resolution_clock::now(); \ 28 | auto elapsed = std::chrono::duration_cast(stop-start); \ 29 | std::cerr << "Section " #Y << " took " << elapsed.count() << "ms." << std::endl; \ 30 | } 31 | 32 | #else 33 | 34 | #define TIME_SECTION_MILLIS(Y, X) X 35 | #define TIME_SECTION_MICRO(Y, X) X 36 | 37 | #endif 38 | 39 | 40 | namespace fbscrypto { 41 | 42 | /** 43 | * Wrapper struct for functions to be computed during bootstrap 44 | */ 45 | struct BootstrapFunction { 46 | public: 47 | 48 | /** 49 | * Constructor for wrapper 50 | * @param a_map lambda/anonymous function for the actual function 51 | * @param message_space used message space, required since the chunksize of the rotation polynomial depends on the function 52 | */ 53 | BootstrapFunction(std::function a_map, uint32_t message_space) : map(std::move(a_map)), message_space(message_space) {} 54 | 55 | /** 56 | * Calls the underlying function 57 | * @param a argument 58 | * @return map(a) 59 | */ 60 | uint32_t operator()(uint32_t a) const { 61 | return map(a); 62 | } 63 | 64 | /** 65 | * Returns the message space which the function uses; 66 | * @return message_space 67 | */ 68 | uint32_t GetMessageSpace() const {return message_space;} 69 | 70 | private: 71 | 72 | std::function map; 73 | uint32_t message_space; 74 | 75 | }; 76 | 77 | } 78 | 79 | 80 | 81 | #endif //FBS_DEFINITIONS_H 82 | -------------------------------------------------------------------------------- /fbs/include/fbscontext.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | // ORIGINAL PALISADE NOTICE 6 | 7 | // @file binfhecontext.h - Header file for FBSFHEContext class, which is used 8 | // for Boolean circuit FHE schemes 9 | // 10 | // @author TPOC: contact@palisade-crypto.org 11 | // @copyright Copyright (c) 2019, Duality Technologies Inc. 12 | // All rights reserved. 13 | // Redistribution and use in source and binary forms, with or without 14 | // modification, are permitted provided that the following conditions are met: 15 | // 1. Redistributions of source code must retain the above copyright notice, 16 | // this list of conditions and the following disclaimer. 17 | // 2. Redistributions in binary form must reproduce the above copyright notice, 18 | // this list of conditions and the following disclaimer in the documentation 19 | // and/or other materials provided with the distribution. THIS SOFTWARE IS 20 | // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 21 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 22 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 23 | // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | // ADDED OBJECTS FOR FDFB: 32 | // - MODIFIED ENCRYPT/DECRYPT FUNCTION 33 | // - RENAMED BINFHECONTEXT to FDFBCONTEXT 34 | // - FBSFHEPARAMSET 35 | // - FBSFHEPARAMSET_LIST 36 | // - FBSFHEPARAMSET_NAMES 37 | // - GETSKN(POLY) METHODS 38 | // - FULLDOMAINBOOTSTRAP 39 | // - FINALIZE 40 | // - HALFDOMAINBOOTSTRAP 41 | // - ENCODE 42 | // - DECODE 43 | // - MODIFIED KEYGEN/KEYGENN METHODS 44 | 45 | 46 | #ifndef FBS_BINFHECONTEXT_H 47 | #define FBS_BINFHECONTEXT_H 48 | 49 | #include 50 | #include 51 | 52 | #include "definitions.h" 53 | #include "fhew.h" 54 | #include "lwe.h" 55 | #include "ringcore.h" 56 | #include "utils/serializable.h" 57 | 58 | namespace fbscrypto { 59 | 60 | 61 | // FDFB/TFHE parameter sets 62 | enum FBSFHEPARAMSET { 63 | 64 | FDFB_80_6 = 1, 65 | FDFB_100_6, 66 | FDFB_80_7, 67 | FDFB_100_7, 68 | FDFB_80_8, 69 | FDFB_100_8, 70 | TFHE_100_7, 71 | TFHE_80_2, 72 | TFHE_100_2, 73 | }; 74 | 75 | // Parameter set list for convenience 76 | static FBSFHEPARAMSET FBSFHEPARAMSET_LIST[] = { 77 | FDFB_80_6, 78 | FDFB_100_6, 79 | FDFB_80_7, 80 | FDFB_100_7, 81 | FDFB_80_8, 82 | FDFB_100_8, 83 | TFHE_100_7, 84 | TFHE_80_2, 85 | TFHE_100_2, 86 | }; 87 | 88 | // Parameter set STRING list for convenience 89 | static std::string FBSFHEPARAMSET_NAMES[] = { 90 | "FDFB_80_6", 91 | "FDFB_100_6", 92 | "FDFB_80_7", 93 | "FDFB_100_7", 94 | "FDFB_80_8", 95 | "FDFB_100_8", 96 | "TFHE_100_7", 97 | "TFHE_80_2", 98 | "TFHE_100_2", 99 | }; 100 | 101 | class LWECiphertextImpl; 102 | 103 | using LWECiphertext = std::shared_ptr; 104 | 105 | using ConstLWECiphertext = const std::shared_ptr; 106 | 107 | class LWEPrivateKeyImpl; 108 | 109 | using LWEPrivateKey = std::shared_ptr; 110 | 111 | using ConstLWEPrivateKey = const std::shared_ptr; 112 | 113 | /** 114 | * @brief FBSFHEContext 115 | * 116 | * The wrapper class for Boolean circuit FHE 117 | */ 118 | class FBSFHEContext { 119 | 120 | public: 121 | FBSFHEContext() {} 122 | 123 | /** 124 | * Creates a crypto context using custom parameters. 125 | * Should be used with care (only for advanced users familiar with LWE 126 | * parameter selection). 127 | * 128 | * @param n lattice parameter for additive LWE scheme 129 | * @param N ring dimension for RingGSW/RLWE used in bootstrapping 130 | * @param &q modulus for additive LWE 131 | * @param &Q modulus for RingGSW/RLWE used in bootstrapping 132 | * @param std standard deviation 133 | * @param baseKS the base used for key switching 134 | * @param baseG the gadget base used in bootstrapping 135 | * @param baseR the base used for refreshing 136 | * @param method the bootstrapping method (AP or GINX) 137 | * @return creates the cryptocontext 138 | */ 139 | void GenerateFDFHEContext(uint32_t n, uint32_t N, const NativeInteger &q, const NativeInteger &Q, double std, 140 | uint32_t baseKS, uint32_t baseG, uint32_t baseBoot, uint32_t basePK, uint32_t HM, 141 | FBSKEYTYPE lweKeyType, FBSKEYTYPE rlweKeyType); 142 | 143 | /** 144 | * Creates a crypto context using predefined parameters sets. Recommended for 145 | * most users. 146 | * 147 | * @param set the parameter set: TOY, MEDIUM, STD128, STD192, STD256 148 | * @param method the bootstrapping method (AP or GINX) 149 | * @return create the cryptocontext 150 | */ 151 | void GenerateFDFHEContext(FBSFHEPARAMSET set, BINFHEMETHOD method = GINX); 152 | 153 | /** 154 | * Gets the refreshing key (used for serialization). 155 | * 156 | * @return a shared pointer to the refreshing key 157 | */ 158 | std::shared_ptr GetRefreshKey() const { 159 | return m_BTKey.BSkey; 160 | } 161 | 162 | /** 163 | * Gets the switching key (used for serialization). 164 | * 165 | * @return a shared pointer to the switching key 166 | */ 167 | std::shared_ptr GetSwitchKey() const { 168 | return m_BTKey.KSkey; 169 | } 170 | 171 | std::shared_ptr GetRLWESwitchKey() const { 172 | return m_BTKey.RLWEKey; 173 | } 174 | 175 | #ifdef WITH_SECRET_KEY 176 | const NativeVector& GetSkN() const { 177 | return m_BTKey.skN; 178 | } 179 | 180 | const lbcrypto::NativePoly& GetSkNPoly() const { 181 | return m_BTKey.RingPoly; 182 | } 183 | #endif 184 | /** 185 | * Generates a secret key for the main LWE scheme 186 | * 187 | * @return a shared pointer to the secret key 188 | */ 189 | LWEPrivateKey KeyGen() const; 190 | 191 | /** 192 | * Generates a secret key used in bootstrapping 193 | * @return a shared pointer to the secret key 194 | */ 195 | LWEPrivateKey KeyGenN() const; 196 | 197 | /** 198 | * Encrypts a bit using a secret key (symmetric key encryption) 199 | * 200 | * @param sk - the secret key 201 | * @param &m - the plaintext 202 | * @param state what kind of ciphertext to generate, Keyswitched, trivial, ... 203 | * @return a shared pointer to the ciphertext 204 | */ 205 | LWECiphertext Encrypt(ConstLWEPrivateKey sk, const LWEPlaintext &m, 206 | CIPHERTEXT_STATE state = FRESH) const; 207 | 208 | LWECiphertext EncryptNoiseless(const LWEPlaintext& m) const; 209 | 210 | /** 211 | * Decrypts a ciphertext using a secret key 212 | * 213 | * @param sk the secret key 214 | * @param ct the ciphertext 215 | * @param *result plaintext result 216 | */ 217 | void Decrypt(ConstLWEPrivateKey sk, ConstLWECiphertext ct, 218 | LWEPlaintext *result) const; 219 | 220 | /** 221 | * Encodes a plaintext to be encrypted 222 | * @param pt message 223 | * @param msg_space message space 224 | * @return round(pt * LWE_q / msg_space) 225 | */ 226 | LWEPlaintext Encode(LWEPlaintext pt, uint32_t msg_space, CIPHERTEXT_STATE from) const; 227 | 228 | /** 229 | * Decodes a plaintext that was encrypted 230 | * @param pt message 231 | * @param msg_space message space 232 | * @return round(pt * msg_space / LWE_q) 233 | */ 234 | LWEPlaintext Decode(LWEPlaintext ct, uint32_t msg_space, CIPHERTEXT_STATE from = CIPHERTEXT_STATE::TRIVIAL) const; 235 | 236 | /** 237 | * Generates a switching key to go from a secret key with (Q,N) to a secret 238 | * key with (q,n) 239 | * 240 | * @param sk new secret key 241 | * @param skN old secret key 242 | * @return a shared pointer to the switching key 243 | */ 244 | std::shared_ptr KeySwitchGen(ConstLWEPrivateKey sk, 245 | ConstLWEPrivateKey skN) const; 246 | 247 | /** 248 | * Generates boostrapping keys 249 | * 250 | * @param sk secret key 251 | */ 252 | void BTKeyGen(ConstLWEPrivateKey sk); 253 | 254 | /** 255 | * Loads bootstrapping keys in the context (typically after deserializing) 256 | * 257 | * @param key struct with the bootstrapping keys 258 | */ 259 | void BTKeyLoad(const RingGSWEvalKey &key) { m_BTKey = key; } 260 | 261 | /** 262 | * Clear the bootstrapping keys in the current context 263 | */ 264 | void ClearBTKeys() { 265 | m_BTKey.BSkey.reset(); 266 | m_BTKey.KSkey.reset(); 267 | } 268 | 269 | LWECiphertext BootstrapBinary(ConstLWECiphertext ct1) const; 270 | /** 271 | * Bootstraps a ciphertext (without peforming any operation) 272 | * 273 | * @param ct1 ciphertext to be bootstrapped 274 | * @return a shared pointer to the resulting ciphertext 275 | */ 276 | LWECiphertext Bootstrap(ConstLWECiphertext ct1) const; 277 | 278 | /** 279 | * Performs a fulldomain bootstrap applying \bootstmap 280 | * @param ct1 ciphertext 281 | * @param bootsMap function wrapper 282 | * @param SKIP_STEP indicates which step of the bootstrapping should be skipped. e.g KEYSWITCHING 283 | * @return LWE(f(Phase(ct1))) 284 | */ 285 | LWECiphertext FullDomainBootstrap(ConstLWECiphertext ct1, BootstrapFunction bootsMap, SKIP_STEP step = NONE) const; 286 | 287 | /** 288 | * Apply the final step skipped in \FullDomainBootstrap 289 | * @param ct1 ciphertext 290 | * @param from step that was skipped 291 | * @return MODSWITCH(ct1) if from == MODSWITCH else KEYSWITCH(MODSWITCH(ct1)) 292 | */ 293 | LWECiphertext Finalize(ConstLWECiphertext ct1, SKIP_STEP from); 294 | 295 | LWECiphertext HalfDomainBootstrap(ConstLWECiphertext ct2, BootstrapFunction bootsMap) const; 296 | 297 | std::shared_ptr GetParams() const { return m_params; } 298 | 299 | std::shared_ptr GetLWEScheme() const { 300 | return m_LWEscheme; 301 | } 302 | 303 | std::shared_ptr GetRingGSWScheme() const { 304 | return m_RingGSWscheme; 305 | } 306 | 307 | template 308 | void save(Archive &ar, std::uint32_t const version) const { 309 | ar(::cereal::make_nvp("params", m_params)); 310 | } 311 | 312 | template 313 | void load(Archive &ar, std::uint32_t const version) { 314 | if (version > SerializedVersion()) { 315 | PALISADE_THROW(lbcrypto::deserialize_error, 316 | "serialized object version " + std::to_string(version) + 317 | " is from a later version of the library"); 318 | } 319 | ar(::cereal::make_nvp("params", m_params)); 320 | } 321 | 322 | std::string SerializedObjectName() const { return "RingGSWBTKey"; } 323 | static uint32_t SerializedVersion() { return 1; } 324 | 325 | // Shared pointer to the underlying additive LWE scheme 326 | std::shared_ptr m_LWEscheme; 327 | private: 328 | // Shared pointer to Ring GSW + LWE parameters 329 | std::shared_ptr m_params; 330 | 331 | // Shared pointer to the underlying RingGSW/RLWE scheme 332 | std::shared_ptr m_RingGSWscheme; 333 | 334 | // Struct containing the bootstrapping keys 335 | RingGSWEvalKey m_BTKey; 336 | }; 337 | 338 | } // namespace fbscrypto 339 | 340 | #endif 341 | -------------------------------------------------------------------------------- /fbs/include/fhew.h: -------------------------------------------------------------------------------- 1 | // ORIGINAL PALISADE NOTICE 2 | 3 | // @file fhew.h - FHEW scheme header file 4 | // The scheme is described in https://eprint.iacr.org/2014/816 and in 5 | // Daniele Micciancio and Yuriy Polyakov, "Bootstrapping in FHEW-like 6 | // Cryptosystems", Cryptology ePrint Archive, Report 2020/086, 7 | // https://eprint.iacr.org/2020/086. 8 | // 9 | // Full reference to https://eprint.iacr.org/2014/816: 10 | // @misc{cryptoeprint:2014:816, 11 | // author = {Leo Ducas and Daniele Micciancio}, 12 | // title = {FHEW: Bootstrapping Homomorphic Encryption in less than a second}, 13 | // howpublished = {Cryptology ePrint Archive, Report 2014/816}, 14 | // year = {2014}, 15 | // note = {\url{https://eprint.iacr.org/2014/816}}, 16 | // @author TPOC: contact@palisade-crypto.org 17 | // 18 | // @copyright Copyright (c) 2019, Duality Technologies Inc. 19 | // All rights reserved. 20 | // Redistribution and use in source and binary forms, with or without 21 | // modification, are permitted provided that the following conditions are met: 22 | // 1. Redistributions of source code must retain the above copyright notice, 23 | // this list of conditions and the following disclaimer. 24 | // 2. Redistributions in binary form must reproduce the above copyright notice, 25 | // this list of conditions and the following disclaimer in the documentation 26 | // and/or other materials provided with the distribution. THIS SOFTWARE IS 27 | // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 28 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 29 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 30 | // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 31 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 32 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 33 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 34 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | 38 | // MODIFICATIONS DONE FOR FDFB 39 | // - SKIP_STEP 40 | // - FULLDOMAINBOOSTRAP 41 | // - FINALIZE 42 | // - RLWEKEYSWITCHGEN 43 | // - RLWEKEYSWITCH 44 | // - BUILDACC 45 | // - COMPUTEPOWERSOFSIG 46 | // - BOOTSTRAPMULTIPLEFUNCTIONS 47 | // - BOOTSTRAPINNER 48 | 49 | #ifndef FBS_FHEW_H 50 | #define FBS_FHEW_H 51 | 52 | #include "lwe.h" 53 | #include "ringcore.h" 54 | #include "definitions.h" 55 | #include "ringswitching.h" 56 | 57 | namespace fbscrypto { 58 | 59 | enum SKIP_STEP { 60 | NONE, 61 | KEYSWITCH, 62 | MODSWITCH 63 | }; 64 | 65 | /** 66 | * @brief Ring GSW accumulator schemes described in 67 | * https://eprint.iacr.org/2014/816 and "Bootstrapping in FHEW-like 68 | * Cryptosystems" 69 | */ 70 | class RingGSWAccumulatorScheme { 71 | public: 72 | RingGSWAccumulatorScheme() {} 73 | 74 | /** 75 | * Generates a refreshing key 76 | * 77 | * @param params a shared pointer to RingGSW scheme parameters 78 | * @param lwescheme a shared pointer to additive LWE scheme 79 | * @param LWEsk a shared pointer to the secret key of the underlying additive 80 | * LWE scheme 81 | * @return a shared pointer to the refreshing key 82 | */ 83 | RingGSWEvalKey KeyGen( 84 | std::shared_ptr params, 85 | std::shared_ptr lwescheme, 86 | std::shared_ptr LWEsk) const; 87 | 88 | /* 89 | * Documentation for these methods match the ones in \fbscontext.h 90 | * 91 | */ 92 | std::shared_ptr 93 | Bootstrap(const std::shared_ptr& params, const RingGSWEvalKey &EK, 94 | const std::shared_ptr& ct1, const std::shared_ptr& LWEscheme, 95 | uint32_t T) const; 96 | 97 | std::shared_ptr 98 | BootstrapBinary(const std::shared_ptr& params, const RingGSWEvalKey& EK, const std::shared_ptr& ct1, const std::shared_ptr& LWEScheme); 99 | 100 | std::shared_ptr 101 | FullDomainBootstrap(const std::shared_ptr ¶ms, const RingGSWEvalKey &EK, 102 | const std::shared_ptr &ct1, 103 | const std::shared_ptr &LWEscheme, const BootstrapFunction &bootsMAP, 104 | SKIP_STEP step = NONE) const; 105 | 106 | std::shared_ptr 107 | Finalize(const std::shared_ptr ¶ms, const RingGSWEvalKey &EK, 108 | std::shared_ptr &scheme, std::shared_ptr ct, 109 | SKIP_STEP from); 110 | 111 | static std::shared_ptr HalfDomainBootstrap( 112 | const std::shared_ptr& params, 113 | const RingGSWEvalKey &EK, 114 | const std::shared_ptr& ct1, 115 | const std::shared_ptr& LWEscheme, 116 | const BootstrapFunction& bootsMAP 117 | ) ; 118 | 119 | /** 120 | * Performs a LWE to RLWE keyswitch 121 | * @param params Ring parameters 122 | * @param K RLWESwitchKey 123 | * @param ctQN LWE ciphertext to be switched 124 | * @return RLWE(m) on input LWE(m) 125 | */ 126 | std::shared_ptr RLWEKeySwitch( 127 | const std::shared_ptr& params, 128 | const std::shared_ptr& K, 129 | const std::shared_ptr& ctQN 130 | ) const; 131 | 132 | private: 133 | /** 134 | * Generates a refreshing key - GINX variant 135 | * 136 | * @param params a shared pointer to RingGSW scheme parameters 137 | * @param lwescheme a shared pointer to additive LWE scheme 138 | * @param LWEsk a shared pointer to the secret key of the underlying additive 139 | * LWE scheme 140 | * @return a shared pointer to the refreshing key 141 | */ 142 | RingGSWEvalKey KeyGenGINX( 143 | std::shared_ptr params, 144 | std::shared_ptr lwescheme, 145 | std::shared_ptr LWEsk) const; 146 | 147 | /** 148 | * Internal RingGSW encryption used in generating the refreshing key - GINX 149 | * variant 150 | * 151 | * @param params a shared pointer to RingGSW scheme parameters 152 | * @param skFFT secret key polynomial in the EVALUATION representation 153 | * @param m plaintext (corresponds to a lookup entry for the LWE scheme secret 154 | * key) 155 | * @return a shared pointer to the resulting ciphertext 156 | */ 157 | std::shared_ptr EncryptGINX( 158 | std::shared_ptr params, 159 | const lbcrypto::NativePoly &skFFT, const LWEPlaintext &m) const; 160 | 161 | /** 162 | * Generates a LWE to RLWE keyswitching key under the same key coefficients 163 | * 164 | * @param params a shared pointer to RingGSW scheme parameters 165 | * @param skN (ring) secret key 166 | */ 167 | std::shared_ptr RLWEKeyswitchGen(std::shared_ptr params, 168 | std::shared_ptr skN) const; 169 | 170 | /** 171 | * Constructs the accumulator used in the full domain bootstrap 172 | * 173 | * @param params a shared pointer to RingGSW scheme parameters 174 | * @param EK a reference to the evaluation key, containing the GINX refresh key, 175 | a LWE to LWE switch key and a LWE to RLWE switch key 176 | * @param powers_of_sig a vector of LWE samples containing \Delta_{Q,t} times the powers of L_boot 177 | * @param rotP0 a rotation polynomial for positive domain, i.e. m \in [0, \frac{q}{2}) 178 | * @param rotP1 a rotation polynomial for negative domain, i.e. m \in [\frac{q}{2}, q) 179 | * @param fct a function which is computed while bootstrapping -- used here to retrieve the message space 180 | * @return RLWE(rotP0 if m \in [0, \frac{q}{2}) else rotP1) 181 | */ 182 | std::shared_ptr BuildAcc( 183 | const std::shared_ptr& params, 184 | const RingGSWEvalKey& EK, 185 | std::vector>& powers_of_sig, 186 | lbcrypto::NativePoly& rotP0, lbcrypto::NativePoly& rotP1, 187 | BootstrapFunction& fct 188 | ) const; 189 | 190 | /** 191 | * Main bootstrapping routine for full domain bootstrapping 192 | * @param params a shared pointer to RingGSW scheme parameters 193 | * @param EK a reference to the evaluation key, containing the GINX refresh key, 194 | a LWE to LWE switch key and a LWE to RLWE switch key 195 | * @param ct1 a ciphertext to be bootstrapped 196 | * @param LWEscheme a shared pointer to the underlying LWE scheme 197 | * @param functions a vector of functions to be computed during the bootstrapping phase -- assumed to work on the same message space 198 | * @return a vector of LWE samples, one for each function which was computed 199 | */ 200 | std::vector> 201 | BootstrapMultipleFunctions(const std::shared_ptr ¶ms, const RingGSWEvalKey &EK, 202 | const std::shared_ptr &ct1, 203 | const std::shared_ptr &LWEscheme, 204 | std::vector &functions, SKIP_STEP step) const; 205 | 206 | std::vector> 207 | ComputePowersOfSig(const shared_ptr ¶ms, const RingGSWEvalKey &EK, 208 | lbcrypto::PolyImpl &sgnP, const NativeVector64 &aN, 209 | const NativeInteger &delta_Qt2) const; 210 | 211 | shared_ptr 212 | BootstrapInner(const std::shared_ptr ¶ms, const RingGSWEvalKey &EK, 213 | const shared_ptr &LWEscheme, const NativeVector64 &aN, 214 | std::shared_ptr &accumulator, SKIP_STEP step) const; 215 | }; 216 | 217 | } // namespace fbscrypto 218 | 219 | #endif 220 | -------------------------------------------------------------------------------- /fbs/include/lwe.h: -------------------------------------------------------------------------------- 1 | // ORIGINAL PALISADE NOTICE 2 | 3 | // @file lwe.h - LWE Encryption Scheme as described in 4 | // https://eprint.iacr.org/2014/816 Full reference: 5 | // @misc{cryptoeprint:2014:816, 6 | // author = {Leo Ducas and Daniele Micciancio}, 7 | // title = {FHEW: Bootstrapping Homomorphic Encryption in less than a second}, 8 | // howpublished = {Cryptology ePrint Archive, Report 2014/816}, 9 | // year = {2014}, 10 | // note = {\url{https://eprint.iacr.org/2014/816}}, 11 | // @author TPOC: contact@palisade-crypto.org 12 | // @copyright Copyright (c) 2019, Duality Technologies Inc. 13 | // All rights reserved. 14 | // Redistribution and use in source and binary forms, with or without 15 | // modification, are permitted provided that the following conditions are met: 16 | // 1. Redistributions of source code must retain the above copyright notice, 17 | // this list of conditions and the following disclaimer. 18 | // 2. Redistributions in binary form must reproduce the above copyright notice, 19 | // this list of conditions and the following disclaimer in the documentation 20 | // and/or other materials provided with the distribution. THIS SOFTWARE IS 21 | // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 22 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 24 | // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | // MODIFICATIONS DONE FOR FDFB 33 | // - CIPHERTEXT STATE 34 | // - MODIFIED ENCRYPT/DECRYPT 35 | // - MODIFIED KEYGEN/KEYGENN 36 | // - ENCODE/DECODE 37 | 38 | #ifndef FBSFHE_LWE_H 39 | #define FBSFHE_LWE_H 40 | 41 | // undefine to output the noise value during decryption 42 | // #define BINFHE_DEBUG 43 | 44 | #include 45 | 46 | #include "lwecore.h" 47 | 48 | namespace fbscrypto { 49 | 50 | /* 51 | * What kind of ciphertext to output when decrypting 52 | */ 53 | enum CIPHERTEXT_STATE { 54 | FRESH, 55 | TRIVIAL, 56 | NOISELESS, 57 | BEFORE_KEYSWITCH, 58 | TRIVIAL_BEFORE_KEYSWITCH, 59 | NOISELESS_BEFORE_KEYSWITCH, 60 | BEFORE_MODSWITCH, 61 | TRIVIAL_BEFORE_MODSWITCH, 62 | NOISELESS_BEFORE_MODSWITCH 63 | }; 64 | 65 | /** 66 | * @brief Additive LWE scheme 67 | */ 68 | class LWEEncryptionScheme { 69 | public: 70 | LWEEncryptionScheme() {} 71 | 72 | /** 73 | * Generates a secret key of dimension n using modulus q 74 | * 75 | * @param params a shared pointer to LWE scheme parameters 76 | * @return a shared pointer to the secret key 77 | */ 78 | std::shared_ptr KeyGen( 79 | std::shared_ptr params) const; 80 | 81 | /** 82 | * Generates a secret key of dimension N using modulus Q 83 | * 84 | * @param params a shared pointer to LWE scheme parameters 85 | * @return a shared pointer to the secret key 86 | */ 87 | std::shared_ptr KeyGenN( 88 | std::shared_ptr params) const; 89 | 90 | /** 91 | * Encrypts a value using a secret key (symmetric key encryption) 92 | * 93 | * @param params a shared pointer to LWE scheme parameters 94 | * @param sk - the secret key 95 | * @param &m - the plaintext 96 | * @return a shared pointer to the ciphertext 97 | */ 98 | std::shared_ptr Encrypt(const std::shared_ptr ¶ms, 99 | const std::shared_ptr &sk, 100 | const LWEPlaintext &m, CIPHERTEXT_STATE state) const; 101 | 102 | /** 103 | * Encrypts a value without a secret key, by setting (a = 0, b = m) 104 | * @param params a shared pointer to LWE scheme parameters 105 | * @param &m - the plaintext 106 | * @return a shared pointer to the ciphertext 107 | */ 108 | std::shared_ptr 109 | EncryptNoiseless(const std::shared_ptr ¶ms, const LWEPlaintext &m, bool prekeyswitch); 110 | 111 | /** 112 | * Decrypts the ciphertext using secret key sk 113 | * @param params a shared pointer to LWE scheme parameters 114 | * @param sk the secret key 115 | * @param ct the ciphertext 116 | * @param *result plaintext result 117 | */ 118 | void Decrypt(const std::shared_ptr& params, 119 | const std::shared_ptr& sk, 120 | const std::shared_ptr& ct, 121 | LWEPlaintext* result) const; 122 | 123 | /** 124 | * Changes an LWE ciphertext modulo Q into an LWE ciphertext modulo q 125 | * 126 | * @param params a shared pointer to LWE scheme parameters 127 | * @param ctQ the input ciphertext 128 | * @return resulting ciphertext 129 | */ 130 | std::shared_ptr ModSwitch( 131 | const std::shared_ptr& params, 132 | const std::shared_ptr& ctQ) const; 133 | 134 | /** 135 | * Generates a switching key to go from a secret key with (Q,N) to a secret 136 | * key with (Q,n). 137 | * 138 | * !! Note that unlike in the GINX/FHEW implementation each digit of the KSK is precomputed, 139 | * !! i.e. one has an N x lKSK x LKSK matrix. 140 | * !! Due to space constraints, we cannot do this so we only compute the usual digit powers 141 | * 142 | * @param params a shared pointer to LWE scheme parameters 143 | * @param sk new secret key 144 | * @param skN old secret key 145 | * @return a shared pointer to the switching key 146 | */ 147 | std::shared_ptr KeySwitchGen( 148 | std::shared_ptr params, 149 | std::shared_ptr sk, 150 | std::shared_ptr skN) const; 151 | 152 | /** 153 | * Switches ciphertext from (Q,N) to (Q,n) 154 | * 155 | * @param params a shared pointer to LWE scheme parameters 156 | * @param K switching key 157 | * @param ctQN input ciphertext 158 | * @return a shared pointer to the resulting ciphertext 159 | */ 160 | std::shared_ptr KeySwitch( 161 | const std::shared_ptr params, 162 | const std::shared_ptr K, 163 | const std::shared_ptr ctQN) const; 164 | }; 165 | 166 | } // namespace fbscrypto 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /fbs/include/ringswitching.h: -------------------------------------------------------------------------------- 1 | // ORIGINAL FILE 2 | 3 | 4 | #ifndef FBS_RINGSWITCHING_H 5 | #define FBS_RINGSWITCHING_H 6 | 7 | //#include 8 | 9 | #include "ringcore.h" 10 | //#include "utils/serializable.h" 11 | 12 | namespace fbscrypto { 13 | 14 | /** 15 | * Class to compute LWE to RLWE switching under the same modulus and coefficient vector dimension. 16 | * This is used while computing the powers of sig. 17 | * Pure container class, actual key generation is delegated to rhe RingGSWAccumulatorScheme 18 | */ 19 | class RLWESwitchingKey { 20 | 21 | public: 22 | 23 | RLWESwitchingKey() {}; 24 | 25 | explicit RLWESwitchingKey(std::vector>> key) : m_key(std::move(key)) { 26 | 27 | } 28 | 29 | explicit RLWESwitchingKey(const RLWESwitchingKey &rhs) { 30 | this->m_key = rhs.m_key; 31 | } 32 | 33 | explicit RLWESwitchingKey(const RLWESwitchingKey &&rhs) { 34 | this->m_key = std::move(rhs.m_key); 35 | } 36 | 37 | const RLWESwitchingKey &operator=(const RLWESwitchingKey &rhs) { 38 | this->m_key = rhs.m_key; 39 | return *this; 40 | } 41 | 42 | const RLWESwitchingKey &operator=(const RLWESwitchingKey &&rhs) { 43 | this->m_key = std::move(rhs.m_key); 44 | return *this; 45 | } 46 | 47 | const std::vector>> &GetElements() 48 | const { 49 | return m_key; 50 | } 51 | 52 | void SetElements(const std::vector>> &key) { 53 | m_key = key; 54 | } 55 | 56 | bool operator==(const RLWESwitchingKey &other) const { 57 | return m_key == other.m_key; 58 | } 59 | 60 | bool operator!=(const RLWESwitchingKey &other) const { 61 | return !(*this == other); 62 | } 63 | 64 | template 65 | void save(Archive &ar, std::uint32_t const version) const { 66 | ar(::cereal::make_nvp("k", m_key)); 67 | } 68 | 69 | template 70 | void load(Archive &ar, std::uint32_t const version) { 71 | if (version > SerializedVersion()) { 72 | PALISADE_THROW(lbcrypto::deserialize_error, 73 | "serialized object version " + std::to_string(version) + 74 | " is from a later version of the library"); 75 | } 76 | 77 | ar(::cereal::make_nvp("k", m_key)); 78 | } 79 | 80 | std::string SerializedObjectName() const { return "RLWESwitchingKey"; } 81 | static uint32_t SerializedVersion() { return 1; } 82 | 83 | private: 84 | 85 | // matrix containing N x lPk RLWE samples 86 | std::vector>> m_key; 87 | 88 | }; 89 | 90 | 91 | } 92 | 93 | #endif //FBS_RINGSWITCHING_H 94 | -------------------------------------------------------------------------------- /fbs/src/fbscontext.cpp: -------------------------------------------------------------------------------- 1 | // ORIGINAL PALISADE NOTICE 2 | // MODIFICATIONS DISCUSSED in fbscontext.h 3 | 4 | // @file binfhecontext.cpp - Implementation file for Boolean Circuit FHE context 5 | // class 6 | // @author TPOC: contact@palisade-crypto.org 7 | // 8 | // @copyright Copyright (c) 2019, Duality Technologies Inc. 9 | // All rights reserved. 10 | // Redistribution and use in source and binary forms, with or without 11 | // modification, are permitted provided that the following conditions are met: 12 | // 1. Redistributions of source code must retain the above copyright notice, 13 | // this list of conditions and the following disclaimer. 14 | // 2. Redistributions in binary form must reproduce the above copyright notice, 15 | // this list of conditions and the following disclaimer in the documentation 16 | // and/or other materials provided with the distribution. THIS SOFTWARE IS 17 | // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 18 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #include "fbscontext.h" 29 | 30 | namespace fbscrypto { 31 | 32 | void FBSFHEContext::GenerateFDFHEContext(uint32_t n, uint32_t N, const NativeInteger &q, const NativeInteger &Q, 33 | double std, 34 | uint32_t baseKS, uint32_t baseG, uint32_t baseBoot, uint32_t basePK, 35 | uint32_t HM, 36 | FBSKEYTYPE lweKeyType, FBSKEYTYPE rlweKeyType) { 37 | 38 | auto lweparams = std::make_shared(n, N, q, Q, std,39, HM, baseKS, lweKeyType, rlweKeyType); 39 | m_params = 40 | std::make_shared(lweparams, baseG, baseBoot, basePK, GINX); 41 | } 42 | 43 | void FBSFHEContext::GenerateFDFHEContext(FBSFHEPARAMSET set, 44 | BINFHEMETHOD method) { 45 | shared_ptr lweparams; 46 | 47 | static NativeInteger Q_1 = 1152921504606748673ull; 48 | static NativeInteger Q_2 = 1152921504606830593ull; 49 | static NativeInteger Q_3 = 281474976546817ull; 50 | static NativeInteger Q_4 = 4294828033ull; 51 | 52 | 53 | switch(set) { 54 | case FDFB_80_6: 55 | lweparams = std::make_shared(700, 1 << 11, 1 << 12, Q_1, 3.19,38, 64, 1 << 6, BINARY, UNIFORM); 56 | m_params = std::make_shared(lweparams, 1 << 11, 1 << 11, 1 << 13, method); 57 | break; 58 | case FDFB_100_6: 59 | lweparams = std::make_shared(1050, 1 << 11, 1 << 12, Q_1, 3.19,41, 64, 1 << 1, BINARY, UNIFORM); 60 | m_params = std::make_shared(lweparams, 1 << 11, 1 << 11, 1 << 13, method); 61 | break; 62 | case FDFB_80_7: 63 | lweparams = std::make_shared(700, 1 << 12, 1 << 13, Q_2, 3.19,39, 64, 1 << 4, BINARY, UNIFORM); 64 | m_params = std::make_shared(lweparams, 1 << 9, 1 << 9, 1 << 12, method); 65 | break; 66 | case FDFB_100_7: 67 | lweparams = std::make_shared(1100, 1 << 12, 1 << 13, Q_2, 3.19,41, 64, 1 << 1, BINARY, UNIFORM); 68 | m_params = std::make_shared(lweparams, 1 << 9, 1 << 9, 1 << 13, method); 69 | break; 70 | case FDFB_80_8: 71 | lweparams = std::make_shared(700, 1 << 13, 1 << 14, Q_2, 3.19, 39,64, 1 << 4, BINARY, UNIFORM); 72 | m_params = std::make_shared(lweparams, 1 << 9, 1 << 8, 1 << 13, method); 73 | break; 74 | case FDFB_100_8: 75 | lweparams = std::make_shared(1100, 1 << 13, 1 << 14, Q_2, 3.19, 41,64, 1 << 1, BINARY, UNIFORM); 76 | m_params = std::make_shared(lweparams, 1 << 9, 1 << 8, 1 << 13, method); 77 | break; 78 | case TFHE_100_7: 79 | lweparams = std::make_shared(1500, 1 << 12, 1 << 13, Q_3, 3.19, 18, 64, 1 << 1, BINARY, UNIFORM); 80 | m_params = std::make_shared(lweparams, 1 << 16, 1 << 8, 1 << 13, method); 81 | break; 82 | case TFHE_80_2: 83 | lweparams = std::make_shared(424, 1 << 10, 1 << 11, Q_4, 3.19,16, 64, 1 << 4, BINARY, UNIFORM); 84 | m_params = std::make_shared(lweparams, 1 << 11, 1 << 12, 1 << 12, method); 85 | break; 86 | case TFHE_100_2: 87 | lweparams = std::make_shared(525, 1 << 10, 1 << 11, Q_4, 3.19,16, 64, 1 << 4, BINARY, UNIFORM); 88 | m_params = std::make_shared(lweparams, 1 << 11, 1 << 12, 1ull << 31, method); 89 | break; 90 | default: 91 | std::string errMsg = "ERROR: No such parameter set exists for FHEW."; 92 | PALISADE_THROW(lbcrypto::config_error, errMsg); 93 | } 94 | } 95 | 96 | LWEPrivateKey FBSFHEContext::KeyGen() const { 97 | return m_LWEscheme->KeyGen(m_params->GetLWEParams()); 98 | } 99 | 100 | LWEPrivateKey FBSFHEContext::KeyGenN() const { 101 | return m_LWEscheme->KeyGenN(m_params->GetLWEParams()); 102 | } 103 | 104 | LWECiphertext FBSFHEContext::Encrypt(ConstLWEPrivateKey sk, 105 | const LWEPlaintext &m, 106 | CIPHERTEXT_STATE output) const { 107 | 108 | return m_LWEscheme->Encrypt(m_params->GetLWEParams(), sk, m, output); 109 | 110 | } 111 | 112 | LWECiphertext FBSFHEContext::EncryptNoiseless(const LWEPlaintext &m) const { 113 | return m_LWEscheme->EncryptNoiseless(m_params->GetLWEParams(), m, false); 114 | } 115 | 116 | LWEPlaintext FBSFHEContext::Encode(LWEPlaintext pt, uint32_t msg_space, CIPHERTEXT_STATE from) const { 117 | long double q; 118 | int64_t QI; 119 | if (from == FRESH or from == TRIVIAL) { 120 | QI = m_params->GetLWEParams()->Getq().ConvertToInt(); 121 | q = QI; 122 | } 123 | else { 124 | QI = m_params->GetLWEParams()->GetQ().ConvertToInt(); 125 | q = QI; 126 | } 127 | 128 | return int64_t(std::round((long double)(pt) * q / (long double)(msg_space))) % QI; 129 | } 130 | 131 | LWEPlaintext FBSFHEContext::Decode(LWEPlaintext ct, uint32_t msg_space, CIPHERTEXT_STATE from) const { 132 | long double q; 133 | int64_t QI; 134 | if (from == FRESH or from == TRIVIAL) { 135 | QI = m_params->GetLWEParams()->Getq().ConvertToInt(); 136 | q = QI; 137 | } 138 | else { 139 | QI = m_params->GetLWEParams()->GetQ().ConvertToInt(); 140 | q = QI; 141 | } 142 | uint32_t d = std::round((double(ct)) * double(msg_space) / q); 143 | return (d) % msg_space; 144 | } 145 | 146 | void FBSFHEContext::Decrypt(ConstLWEPrivateKey sk, ConstLWECiphertext ct, 147 | LWEPlaintext *result) const { 148 | return m_LWEscheme->Decrypt(m_params->GetLWEParams(), sk, ct, result); 149 | } 150 | 151 | std::shared_ptr FBSFHEContext::KeySwitchGen( 152 | ConstLWEPrivateKey sk, ConstLWEPrivateKey skN) const { 153 | return m_LWEscheme->KeySwitchGen(m_params->GetLWEParams(), sk, skN); 154 | } 155 | 156 | void FBSFHEContext::BTKeyGen(ConstLWEPrivateKey sk) { 157 | m_BTKey = m_RingGSWscheme->KeyGen(m_params, m_LWEscheme, sk); 158 | } 159 | 160 | LWECiphertext FBSFHEContext::BootstrapBinary(ConstLWECiphertext ct1) const { 161 | return m_RingGSWscheme->BootstrapBinary(m_params, m_BTKey, ct1, m_LWEscheme); 162 | } 163 | 164 | LWECiphertext FBSFHEContext::Bootstrap(ConstLWECiphertext ct1) const { 165 | return m_RingGSWscheme->Bootstrap(m_params, m_BTKey, ct1, m_LWEscheme, 64); 166 | } 167 | 168 | LWECiphertext FBSFHEContext::Finalize(ConstLWECiphertext ct1, SKIP_STEP from) { 169 | return m_RingGSWscheme->Finalize(m_params, m_BTKey, m_LWEscheme, ct1, from); 170 | } 171 | 172 | LWECiphertext 173 | FBSFHEContext::FullDomainBootstrap(ConstLWECiphertext ct1, BootstrapFunction bootsMap, SKIP_STEP step) const { 174 | TIME_SECTION_MILLIS("FDB", auto result = m_RingGSWscheme->FullDomainBootstrap(m_params, m_BTKey, ct1, 175 | m_LWEscheme, bootsMap, step);) 176 | return result; 177 | } 178 | 179 | LWECiphertext FBSFHEContext::HalfDomainBootstrap(ConstLWECiphertext ct2, BootstrapFunction bootsMap) const { 180 | TIME_SECTION_MILLIS("HDB", auto result = m_RingGSWscheme->HalfDomainBootstrap(m_params, m_BTKey, ct2, m_LWEscheme, bootsMap)); 181 | return result; 182 | } 183 | } // namespace fbscrypto 184 | -------------------------------------------------------------------------------- /fbs/src/fhew.cpp: -------------------------------------------------------------------------------- 1 | // ORIGINAL PALISADE NOTICE 2 | 3 | // @file fhew.cpp - FHEW scheme (RingGSW accumulator) implementation 4 | // The scheme is described in https://eprint.iacr.org/2014/816 and in 5 | // Daniele Micciancio and Yuriy Polyakov, "Bootstrapping in FHEW-like 6 | // Cryptosystems", Cryptology ePrint Archive, Report 2020/086, 7 | // https://eprint.iacr.org/2020/086. 8 | // 9 | // Full reference to https://eprint.iacr.org/2014/816: 10 | // @misc{cryptoeprint:2014:816, 11 | // author = {Leo Ducas and Daniele Micciancio}, 12 | // title = {FHEW: Bootstrapping Homomorphic Encryption in less than a second}, 13 | // howpublished = {Cryptology ePrint Archive, Report 2014/816}, 14 | // year = {2014}, 15 | // note = {\url{https://eprint.iacr.org/2014/816}}, 16 | // @author TPOC: contact@palisade-crypto.org 17 | // 18 | // @copyright Copyright (c) 2019, Duality Technologies Inc. 19 | // All rights reserved. 20 | // Redistribution and use in source and binary forms, with or without 21 | // modification, are permitted provided that the following conditions are met: 22 | // 1. Redistributions of source code must retain the above copyright notice, 23 | // this list of conditions and the following disclaimer. 24 | // 2. Redistributions in binary form must reproduce the above copyright notice, 25 | // this list of conditions and the following disclaimer in the documentation 26 | // and/or other materials provided with the distribution. THIS SOFTWARE IS 27 | // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 28 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 29 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 30 | // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 31 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 32 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 33 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 34 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | 38 | // MODIFICATIONS FOR FDFB 39 | // - REMOVED MOST ROUTINES THAT WERE NOT USED 40 | 41 | #include "fhew.h" 42 | 43 | namespace fbscrypto { 44 | 45 | // Encryption for the GINX variant, as described in "Bootstrapping in FHEW-like 46 | // Cryptosystems" 47 | std::shared_ptr RingGSWAccumulatorScheme::EncryptGINX( 48 | const std::shared_ptr params, const lbcrypto::NativePoly &skNTT, 49 | const LWEPlaintext &m) const { 50 | NativeInteger Q = params->GetLWEParams()->GetQ(); 51 | uint32_t digitsG = params->GetDigitsG(); 52 | uint32_t digitsG2 = params->GetDigitsG2(); 53 | const shared_ptr polyParams = params->GetPolyParams(); 54 | 55 | auto result = std::make_shared(digitsG2, 2); 56 | 57 | lbcrypto::DiscreteUniformGeneratorImpl dug; 58 | dug.SetModulus(Q); 59 | 60 | // tempA is introduced to minimize the number of NTTs 61 | std::vector tempA(digitsG2); 62 | 63 | for (uint32_t i = 0; i < digitsG2; ++i) { 64 | (*result)[i][0] = lbcrypto::NativePoly(dug, polyParams, Format::COEFFICIENT); 65 | tempA[i] = (*result)[i][0]; 66 | #ifdef WITH_NOISE 67 | (*result)[i][1] = lbcrypto::NativePoly(params->GetLWEParams()->GetDgg(), polyParams, 68 | Format::COEFFICIENT); 69 | #else 70 | (*result)[i][1] = lbcrypto::NativePoly(polyParams, COEFFICIENT, true); 71 | #endif 72 | } 73 | 74 | for (uint32_t i = 0; i < digitsG; ++i) { 75 | if (m > 0) { 76 | // Add G Multiple 77 | (*result)[2 * i][0][0].ModAddEq(params->GetGPower()[i], Q); 78 | // [a,as+e] + G 79 | (*result)[2 * i + 1][1][0].ModAddEq(params->GetGPower()[i], Q); 80 | } 81 | } 82 | 83 | // 3*digitsG2 NTTs are called 84 | result->SetFormat(Format::EVALUATION); 85 | for (uint32_t i = 0; i < digitsG2; ++i) { 86 | tempA[i].SetFormat(Format::EVALUATION); 87 | (*result)[i][1] += tempA[i] * skNTT; 88 | } 89 | 90 | return result; 91 | } 92 | 93 | // wrapper for KeyGen methods 94 | RingGSWEvalKey RingGSWAccumulatorScheme::KeyGen( 95 | const std::shared_ptr params, 96 | const std::shared_ptr lwescheme, 97 | const std::shared_ptr LWEsk) const { 98 | 99 | return KeyGenGINX(params, lwescheme, LWEsk); 100 | } 101 | 102 | // Bootstrapping keys generation for the GINX variant, as described in 103 | // "Bootstrapping in FHEW-like Cryptosystems" 104 | RingGSWEvalKey RingGSWAccumulatorScheme::KeyGenGINX( 105 | const std::shared_ptr params, 106 | const std::shared_ptr lwescheme, 107 | const std::shared_ptr LWEsk) const { 108 | 109 | RingGSWEvalKey ek; 110 | const std::shared_ptr skN = 111 | lwescheme->KeyGenN(params->GetLWEParams()); 112 | 113 | ek.KSkey = lwescheme->KeySwitchGen(params->GetLWEParams(), LWEsk, skN); 114 | ek.RLWEKey = RLWEKeyswitchGen(params, skN); 115 | 116 | lbcrypto::NativePoly skNPoly = lbcrypto::NativePoly(params->GetPolyParams()); 117 | skNPoly.SetValues(skN->GetElement(), Format::COEFFICIENT); 118 | 119 | 120 | #ifdef WITH_SECRET_KEY 121 | ek.skN = NativeVector(skN->GetElement()); 122 | ek.RingPoly = lbcrypto::NativePoly(params->GetPolyParams()); 123 | ek.RingPoly.SetValues(skN->GetElement(), COEFFICIENT); 124 | ek.RingPoly.SetFormat(EVALUATION); 125 | std::cerr << "[GEN]" << LWEsk->GetElement() << std::endl; 126 | std::cerr << "[GEN] Private RLWE Key" << ek.skN << std::endl; 127 | auto copy = lbcrypto::NativePoly(ek.RingPoly); 128 | copy.SetFormat(COEFFICIENT); 129 | std::cerr << "[GEN] Private RLWE Poly " << skNPoly << std::endl; 130 | #endif 131 | 132 | 133 | skNPoly.SetFormat(Format::EVALUATION); 134 | 135 | uint64_t q = params->GetLWEParams()->Getq().ConvertToInt(); 136 | uint32_t n = params->GetLWEParams()->Getn(); 137 | 138 | ek.BSkey = std::make_shared(1, 2, n); 139 | 140 | int64_t qHalf = (q >> 1); 141 | 142 | // handles ternary secrets using signed mod 3 arithmetic; 0 -> {0,0}, 1 -> 143 | // {1,0}, -1 -> {0,1} 144 | #pragma omp parallel for 145 | for (uint32_t i = 0; i < n; ++i) { 146 | int64_t s = LWEsk->GetElement()[i].ConvertToInt(); 147 | if (s > qHalf) s -= q; 148 | switch (s) { 149 | case 0: 150 | (*ek.BSkey)[0][0][i] = *(EncryptGINX(params, skNPoly, 0)); 151 | (*ek.BSkey)[0][1][i] = *(EncryptGINX(params, skNPoly, 0)); 152 | break; 153 | case 1: 154 | (*ek.BSkey)[0][0][i] = *(EncryptGINX(params, skNPoly, 1)); 155 | (*ek.BSkey)[0][1][i] = *(EncryptGINX(params, skNPoly, 0)); 156 | break; 157 | case -1: 158 | (*ek.BSkey)[0][0][i] = *(EncryptGINX(params, skNPoly, 0)); 159 | (*ek.BSkey)[0][1][i] = *(EncryptGINX(params, skNPoly, 1)); 160 | break; 161 | default: 162 | std::string errMsg = 163 | "ERROR: only ternary secret key distributions are supported."; 164 | PALISADE_THROW(lbcrypto::not_implemented_error, errMsg); 165 | } 166 | } 167 | 168 | return ek; 169 | } 170 | 171 | 172 | }; // namespace fbscrypto 173 | -------------------------------------------------------------------------------- /fbs/src/lwe.cpp: -------------------------------------------------------------------------------- 1 | // @file lwe.cpp - LWE Encryption Scheme implementation as described in 2 | // https://eprint.iacr.org/2014/816 Full reference: 3 | // @misc{cryptoeprint:2014:816, 4 | // author = {Leo Ducas and Daniele Micciancio}, 5 | // title = {FHEW: Bootstrapping Homomorphic Encryption in less than a second}, 6 | // howpublished = {Cryptology ePrint Archive, Report 2014/816}, 7 | // year = {2014}, 8 | // note = {\url{https://eprint.iacr.org/2014/816}}, 9 | // @author TPOC: contact@palisade-crypto.org 10 | // @copyright Copyright (c) 2019, Duality Technologies Inc. 11 | // All rights reserved. 12 | // Redistribution and use in source and binary forms, with or without 13 | // modification, are permitted provided that the following conditions are met: 14 | // 1. Redistributions of source code must retain the above copyright notice, 15 | // this list of conditions and the following disclaimer. 16 | // 2. Redistributions in binary form must reproduce the above copyright notice, 17 | // this list of conditions and the following disclaimer in the documentation 18 | // and/or other materials provided with the distribution. THIS SOFTWARE IS 19 | // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 20 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 21 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 22 | // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | #include 31 | #include "lwe.h" 32 | #include "math/binaryuniformgenerator.h" 33 | #include "math/discreteuniformgenerator.h" 34 | #include "math/ternaryuniformgenerator.h" 35 | 36 | namespace fbscrypto { 37 | 38 | std::shared_ptr LWEEncryptionScheme::KeyGen( 39 | const std::shared_ptr params) const { 40 | 41 | NativeVector vec; 42 | switch (params->GetLweType()) { 43 | case BINARY: { 44 | lbcrypto::BinaryUniformGeneratorImpl gen; 45 | vec = gen.GenerateVector(params->Getn(), params->Getq()); 46 | break; 47 | } 48 | case TERNARY: { 49 | lbcrypto::TernaryUniformGeneratorImpl tug; 50 | vec = tug.GenerateVector(params->Getn(), params->Getq()); 51 | break; 52 | } 53 | default: 54 | PALISADE_THROW(lbcrypto::config_error, "Invalid Key Type !"); 55 | } 56 | 57 | // find nonzero indices 58 | std::vector nonzero_idx; 59 | for(uint32_t i = 0; i < params->Getn(); i++) { 60 | if (vec.at(i) != 0) 61 | nonzero_idx.push_back(i); 62 | } 63 | 64 | // not secure ! 65 | auto rng = std::default_random_engine {}; 66 | // shuffle indices 67 | std::shuffle(nonzero_idx.begin(), nonzero_idx.end(), rng); 68 | 69 | // remove necessary amount of entries to make key sparse 70 | int to_delete = (int)nonzero_idx.size() - (int)params->GetHM(); 71 | nonzero_idx.resize(std::max(to_delete, 0)); 72 | for(uint32_t i : nonzero_idx) { 73 | vec[i] = 0; 74 | } 75 | 76 | return std::make_shared( 77 | LWEPrivateKeyImpl(vec)); 78 | } 79 | 80 | std::shared_ptr LWEEncryptionScheme::KeyGenN( 81 | const std::shared_ptr params) const { 82 | 83 | NativeVector vec; 84 | switch (params->GetRlweType()) { 85 | case BINARY: { 86 | lbcrypto::BinaryUniformGeneratorImpl gen; 87 | vec = gen.GenerateVector(params->GetN(), params->GetQ()); 88 | break; 89 | } 90 | case TERNARY: { 91 | lbcrypto::TernaryUniformGeneratorImpl tug; 92 | vec = tug.GenerateVector(params->GetN(), params->GetQ()); 93 | break; 94 | } 95 | case UNIFORM: { 96 | lbcrypto::DiscreteUniformGeneratorImpl gen; 97 | gen.SetModulus(params->GetQ()); 98 | vec = gen.GenerateVector(params->GetN()); 99 | break; 100 | } 101 | default: 102 | PALISADE_THROW(lbcrypto::config_error, "Invalid Key Type !"); 103 | } 104 | 105 | return std::make_shared( 106 | LWEPrivateKeyImpl(vec)); 107 | 108 | } 109 | 110 | std::shared_ptr LWEEncryptionScheme::Encrypt(const std::shared_ptr ¶ms, 111 | const std::shared_ptr &sk, 112 | const LWEPlaintext &m, CIPHERTEXT_STATE state) const { 113 | 114 | NativeInteger q; 115 | uint32_t n; 116 | NativeInteger b = m; 117 | 118 | if (state == TRIVIAL or state == FRESH) { 119 | q = params->Getq(); 120 | n = params->Getn(); 121 | } 122 | else if (state == BEFORE_MODSWITCH or state == TRIVIAL_BEFORE_MODSWITCH or state == NOISELESS_BEFORE_MODSWITCH) { 123 | q = params->GetQ(); 124 | n = params->Getn(); 125 | } else { 126 | q = params->GetQ(); 127 | n = params->GetN(); 128 | } 129 | 130 | if (state == FRESH or state == BEFORE_MODSWITCH or state == BEFORE_KEYSWITCH) { 131 | b.ModAddFastEq(params->GetDgg().GenerateInteger(q), q); 132 | } 133 | 134 | NativeVector a(n, q); 135 | if (state == TRIVIAL or state == TRIVIAL_BEFORE_MODSWITCH or state == TRIVIAL_BEFORE_KEYSWITCH or state == BEFORE_KEYSWITCH 136 | or state == NOISELESS_BEFORE_KEYSWITCH) { 137 | for(uint32_t i = 0; i < n; i++) 138 | a[i] = 0; 139 | return std::make_shared(a, b); 140 | 141 | } 142 | 143 | lbcrypto::DiscreteUniformGeneratorImpl dug; 144 | dug.SetModulus(q); 145 | a = dug.GenerateVector(n); 146 | 147 | NativeInteger mu = q.ComputeMu(); 148 | 149 | const NativeVector &s = sk->GetElement(); 150 | 151 | for (uint32_t i = 0; i < n; ++i) { 152 | NativeInteger elem = s[i] > 1 ? q - 1 : s[i]; 153 | b += a[i].ModMulFast(elem, q, mu); 154 | } 155 | 156 | b.ModEq(q); 157 | 158 | return std::make_shared(LWECiphertextImpl(a, b)); 159 | } 160 | 161 | std::shared_ptr 162 | LWEEncryptionScheme::EncryptNoiseless(const std::shared_ptr ¶ms, const LWEPlaintext &m, 163 | bool prekeyswitch) { 164 | 165 | NativeVector a(params->Getn(), prekeyswitch ? params->GetQ() : params->Getq()); 166 | for(uint32_t i = 0; i < params->Getn(); i++) 167 | a[i] = 0; 168 | 169 | NativeInteger b = m; 170 | 171 | return std::make_shared(LWECiphertextImpl(a,b)); 172 | } 173 | 174 | // classical LWE decryption 175 | // m_result = Round(4/q * (b - a*s)) 176 | void LWEEncryptionScheme::Decrypt( 177 | const std::shared_ptr& params, 178 | const std::shared_ptr& sk, 179 | const std::shared_ptr& ct, 180 | LWEPlaintext *result) const { 181 | // TODO in the future we should add a check to make sure sk parameters match 182 | // the ct parameters 183 | 184 | // Create local variables to speed up the computations 185 | NativeVector a = ct->GetA(); 186 | uint32_t n = sk->GetElement().GetLength(); 187 | NativeVector s = sk->GetElement(); 188 | NativeInteger q = sk->GetElement().GetModulus(); 189 | 190 | NativeInteger mu = q.ComputeMu(); 191 | 192 | NativeInteger inner(0); 193 | for (uint32_t i = 0; i < n; ++i) { 194 | inner += a[i].ModMulFast(s[i], q, mu); 195 | } 196 | inner.ModEq(q); 197 | 198 | NativeInteger r = ct->GetB(); 199 | 200 | r.ModSubFastEq(inner, q); 201 | 202 | *result = r.ConvertToInt(); 203 | 204 | } 205 | 206 | // the main rounding operation used in ModSwitch (as described in Section 3 of 207 | // https://eprint.iacr.org/2014/816) The idea is that Round(x) = 0.5 + Floor(x) 208 | NativeInteger RoundqQ(const NativeInteger &v, const NativeInteger &q, 209 | const NativeInteger &Q) { 210 | return NativeInteger((uint64_t)std::floor(0.5 + v.ConvertToDouble() * 211 | q.ConvertToDouble() / 212 | Q.ConvertToDouble())) 213 | .Mod(q); 214 | } 215 | 216 | // Modulus switching - directly applies the scale-and-round operation RoundQ 217 | std::shared_ptr LWEEncryptionScheme::ModSwitch( 218 | const std::shared_ptr& params, 219 | const std::shared_ptr& ctQ) const { 220 | NativeVector a(params->Getn(), params->Getq()); 221 | 222 | uint32_t n = params->Getn(); 223 | NativeInteger q = params->Getq(); 224 | NativeInteger Q = params->GetQ(); 225 | 226 | for (uint32_t i = 0; i < n; ++i) a[i] = RoundqQ(ctQ->GetA()[i], q, Q); 227 | 228 | NativeInteger b = RoundqQ(ctQ->GetB(), q, Q); 229 | 230 | return std::make_shared(LWECiphertextImpl(a, b)); 231 | } 232 | 233 | std::shared_ptr LWEEncryptionScheme::KeySwitchGen( 234 | const std::shared_ptr params, 235 | const std::shared_ptr sk, 236 | const std::shared_ptr skN) const { 237 | 238 | // Create local copies of main variables 239 | uint32_t n = params->Getn(); 240 | uint32_t N = params->GetN(); 241 | NativeInteger Q = params->GetQ(); 242 | std::vector digitsKS = params->GetDigitsKS(); 243 | uint32_t expKS = digitsKS.size(); 244 | 245 | // newSK stores negative values using modulus q 246 | // we need to switch to modulus Q 247 | NativeVector newSK = sk->GetElement(); 248 | newSK.SwitchModulus(Q); 249 | 250 | NativeVector oldSK = skN->GetElement(); 251 | 252 | lbcrypto::DiscreteUniformGeneratorImpl dug; 253 | dug.SetModulus(Q); 254 | 255 | NativeInteger mu = Q.ComputeMu(); 256 | 257 | std::vector> resultVec(N); 258 | auto dgg = params->GetKSKDgg(); 259 | 260 | #pragma omp parallel for 261 | for(uint32_t i = 0; i < N; ++i) { 262 | std::vector vector1(expKS); 263 | for (uint32_t j = 0; j < expKS; ++j) { 264 | NativeInteger b = oldSK[i].ModMul(digitsKS[j], Q); 265 | #ifdef WITH_NOISE 266 | b.ModAddFastEq(dgg.GenerateInteger(Q), Q); 267 | #endif 268 | NativeVector a = dug.GenerateVector(n); 269 | for (int k = 0; k < n; ++k) { 270 | b += a[k].ModMulFast(newSK[k], Q, mu); 271 | } 272 | b.ModEq(Q); 273 | 274 | vector1[j] = LWECiphertextImpl(a, b); 275 | } 276 | resultVec[i] = std::move(vector1); 277 | } 278 | 279 | return std::make_shared(resultVec); 280 | } 281 | 282 | // the key switching operation as described in Section 3 of 283 | // https://eprint.iacr.org/2014/816 284 | std::shared_ptr LWEEncryptionScheme::KeySwitch( 285 | const std::shared_ptr params, 286 | const std::shared_ptr K, 287 | const std::shared_ptr ctQN) const { 288 | uint32_t n = params->Getn(); 289 | uint32_t N = params->GetN(); 290 | NativeInteger Q = params->GetQ(); 291 | uint32_t baseKS = params->GetBaseKS(); 292 | std::vector digitsKS = params->GetDigitsKS(); 293 | uint32_t expKS = digitsKS.size(); 294 | 295 | // creates an empty vector 296 | NativeVector a(n, Q); 297 | NativeInteger b = ctQN->GetB(); 298 | NativeVector aOld = ctQN->GetA(); 299 | 300 | NativeInteger mu = Q.ComputeMu(); 301 | 302 | for (uint32_t i = 0; i < N; ++i) { 303 | NativeInteger atmp = aOld[i]; 304 | for (uint32_t j = 0; j < expKS; ++j, atmp /= baseKS) { 305 | uint64_t a0 = (atmp % baseKS).ConvertToInt(); 306 | auto& sample = K->GetElements()[i][j]; 307 | auto& A = sample.GetA(); 308 | auto& B = sample.GetB(); 309 | 310 | for (uint32_t k = 0; k < n; ++k) 311 | a[k].ModSubFastEq(A[k].ModMulFast(a0, Q, mu), Q); 312 | 313 | b.ModSubFastEq(B.ModMulFast(a0, Q, mu), Q); 314 | } 315 | } 316 | 317 | return std::make_shared(LWECiphertextImpl(a, b)); 318 | } 319 | 320 | }; // namespace fbscrypto 321 | -------------------------------------------------------------------------------- /fbs/src/ringswitching.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 06.05.21. 3 | // 4 | 5 | #include "ringswitching.h" 6 | #include "fhew.h" 7 | 8 | namespace fbscrypto { 9 | 10 | std::shared_ptr RingGSWAccumulatorScheme::RLWEKeyswitchGen( 11 | const std::shared_ptr params, 12 | const std::shared_ptr skN) const { 13 | 14 | uint32_t N = params->GetLWEParams()->GetN(); 15 | NativeInteger Q = params->GetLWEParams()->GetQ(); 16 | NativeInteger mu = Q.ComputeMu(); 17 | 18 | auto skPoly = lbcrypto::NativePoly (params->GetPolyParams(), COEFFICIENT); 19 | skPoly.SetValues(skN->GetElement(), COEFFICIENT); 20 | 21 | skPoly.SetFormat(EVALUATION); 22 | 23 | auto expPK = params->GetDigitsPK(); 24 | 25 | lbcrypto::DiscreteUniformGeneratorImpl dug; 26 | dug.SetModulus(Q); 27 | 28 | std::vector>> resultVec(N); 29 | for(uint32_t i = 0; i < N; i++) { 30 | std::vector> vector1(expPK); 31 | 32 | auto ski = skN->GetElement()[i]; 33 | 34 | for(uint32_t j = 0; j < expPK; j++) { 35 | 36 | auto result = std::make_shared(1, 2); 37 | (*result)[0][0] = lbcrypto::NativePoly(dug, params->GetPolyParams(), EVALUATION); 38 | #ifdef WITH_NOISE 39 | (*result)[0][1] = lbcrypto::NativePoly(params->GetLWEParams()->GetDgg(), params->GetPolyParams(), COEFFICIENT); 40 | #else 41 | (*result)[0][1] = lbcrypto::NativePoly(params->GetPolyParams(), COEFFICIENT, true); 42 | #endif 43 | (*result)[0][1][0].ModAddEq(ski.ModMulFast(params->GetPKPower()[j], Q, mu), Q); 44 | 45 | (*result)[0][1].SetFormat(EVALUATION); 46 | (*result)[0][1] += (*result)[0][0] * skPoly; 47 | 48 | vector1[j] = result; 49 | } 50 | 51 | resultVec[i] = std::move(vector1); 52 | } 53 | 54 | return std::make_shared(resultVec); 55 | } 56 | 57 | std::shared_ptr RingGSWAccumulatorScheme::RLWEKeySwitch( 58 | const std::shared_ptr& params, const std::shared_ptr& K, 59 | const std::shared_ptr& ctQN) const{ 60 | 61 | auto result = std::make_shared(1, 2); 62 | 63 | (*result)[0][0] = lbcrypto::NativePoly(params->GetPolyParams(), EVALUATION, true); 64 | (*result)[0][1] = lbcrypto::NativePoly(params->GetPolyParams(), COEFFICIENT, true); 65 | (*result)[0][1][0] = ctQN->GetB(); 66 | (*result)[0][1].SetFormat(EVALUATION); 67 | 68 | uint32_t basePK = params->GetBasePK(); 69 | uint32_t expPK = params->GetDigitsPK(); 70 | uint32_t N = params->GetLWEParams()->GetN(); 71 | auto& key = (*K).GetElements(); 72 | 73 | for(uint32_t i = 0; i < N; i++) { 74 | auto atmp = ctQN->GetA(i).ConvertToInt(); 75 | for(uint32_t j = 0; j < expPK; j++, atmp /= basePK) { 76 | auto a0 = atmp % basePK; 77 | auto& sample = key[i][j]; 78 | 79 | if (a0 > 0) { 80 | (*result)[0][0] -= a0 * (*sample)[0][0]; 81 | (*result)[0][1] -= a0 * (*sample)[0][1]; 82 | } 83 | } 84 | } 85 | 86 | return result; 87 | } 88 | } -------------------------------------------------------------------------------- /nn_data/MNIST_1_6/config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "dense", 4 | "activation": "relu_modulo", 5 | "rows": 784, 6 | "columns": 512, 7 | "weights": "layers/L0_dense_W.csv", 8 | "bias": "layers/L0_dense_B.csv", 9 | "modulus": 1024, 10 | "scale": 1, 11 | "scale_inv": 0 12 | }, 13 | { 14 | "name": "dense", 15 | "activation": "relu_modulo", 16 | "rows": 512, 17 | "columns": 510, 18 | "weights": "layers/L1_dense_W.csv", 19 | "bias": "layers/L1_dense_B.csv", 20 | "modulus": 1024, 21 | "scale": 6, 22 | "scale_inv": 0 23 | }, 24 | { 25 | "name": "dense", 26 | "activation": "softmax_modulo", 27 | "rows": 510, 28 | "columns": 10, 29 | "weights": "layers/L2_dense_W.csv", 30 | "bias": "layers/L2_dense_B.csv", 31 | "modulus": 1024, 32 | "scale": 6, 33 | "scale_inv": 0 34 | } 35 | ] -------------------------------------------------------------------------------- /nn_data/MNIST_1_6/dataset/mnist_100_labels.csv: -------------------------------------------------------------------------------- 1 | 3 2 | 7 3 | 4 4 | 0 5 | 3 6 | 4 7 | 9 8 | 8 9 | 6 10 | 9 11 | 9 12 | 7 13 | 0 14 | 0 15 | 7 16 | 1 17 | 1 18 | 9 19 | 2 20 | 1 21 | 3 22 | 7 23 | 2 24 | 6 25 | 5 26 | 1 27 | 2 28 | 8 29 | 4 30 | 3 31 | 8 32 | 7 33 | 6 34 | 7 35 | 2 36 | 5 37 | 0 38 | 5 39 | 8 40 | 1 41 | 4 42 | 1 43 | 5 44 | 7 45 | 2 46 | 9 47 | 0 48 | 8 49 | 2 50 | 5 51 | 6 52 | 9 53 | 6 54 | 9 55 | 6 56 | 7 57 | 7 58 | 5 59 | 1 60 | 6 61 | 3 62 | 2 63 | 8 64 | 4 65 | 8 66 | 8 67 | 8 68 | 5 69 | 5 70 | 7 71 | 2 72 | 5 73 | 7 74 | 0 75 | 1 76 | 1 77 | 7 78 | 1 79 | 9 80 | 3 81 | 8 82 | 1 83 | 6 84 | 9 85 | 8 86 | 2 87 | 7 88 | 5 89 | 8 90 | 9 91 | 0 92 | 9 93 | 1 94 | 7 95 | 6 96 | 1 97 | 8 98 | 3 99 | 4 100 | 3 101 | -------------------------------------------------------------------------------- /nn_data/MNIST_1_6/layers/L0_dense_B.csv: -------------------------------------------------------------------------------- 1 | -1 2 | 0 3 | 0 4 | 0 5 | -1 6 | 0 7 | 0 8 | 0 9 | -1 10 | 0 11 | -1 12 | 0 13 | 0 14 | -1 15 | -1 16 | 0 17 | 0 18 | 0 19 | -1 20 | 0 21 | 0 22 | -1 23 | 0 24 | -1 25 | -2 26 | 0 27 | -1 28 | 1 29 | 0 30 | 0 31 | 1 32 | -1 33 | 0 34 | 0 35 | -1 36 | 0 37 | 0 38 | -1 39 | 0 40 | 0 41 | 0 42 | 0 43 | 1 44 | 0 45 | 0 46 | 0 47 | 0 48 | 0 49 | 0 50 | 0 51 | -1 52 | 0 53 | 0 54 | 1 55 | 0 56 | 0 57 | 0 58 | 0 59 | 1 60 | 0 61 | 0 62 | 0 63 | -1 64 | 0 65 | 1 66 | -1 67 | 0 68 | -1 69 | -1 70 | 1 71 | 0 72 | 0 73 | -1 74 | 0 75 | -1 76 | 0 77 | 0 78 | -1 79 | 0 80 | 0 81 | 0 82 | -1 83 | -1 84 | 1 85 | 0 86 | 1 87 | 0 88 | 1 89 | -1 90 | 0 91 | -1 92 | 1 93 | -1 94 | 0 95 | 0 96 | 1 97 | -1 98 | 0 99 | -1 100 | 1 101 | -1 102 | 0 103 | 0 104 | 0 105 | 0 106 | -1 107 | 0 108 | 2 109 | -1 110 | -1 111 | 1 112 | 0 113 | -1 114 | 1 115 | 0 116 | -1 117 | 0 118 | 0 119 | 1 120 | 0 121 | -1 122 | -1 123 | 0 124 | 0 125 | 0 126 | 0 127 | 0 128 | 0 129 | -1 130 | 0 131 | 0 132 | 0 133 | -1 134 | 0 135 | 0 136 | 0 137 | -1 138 | -1 139 | 1 140 | 0 141 | 1 142 | 0 143 | 0 144 | -1 145 | -1 146 | 1 147 | 1 148 | 1 149 | -1 150 | 0 151 | 0 152 | 0 153 | -1 154 | 0 155 | 0 156 | -1 157 | -1 158 | 0 159 | 1 160 | 0 161 | 1 162 | 0 163 | 0 164 | 0 165 | 0 166 | 0 167 | 0 168 | 1 169 | 0 170 | -1 171 | 0 172 | 0 173 | -1 174 | 0 175 | 1 176 | -2 177 | 1 178 | 0 179 | -1 180 | 0 181 | 1 182 | 0 183 | 0 184 | 0 185 | 0 186 | 0 187 | 0 188 | 0 189 | 0 190 | 0 191 | 0 192 | 0 193 | -1 194 | -1 195 | 0 196 | -1 197 | 0 198 | -1 199 | 0 200 | 0 201 | 0 202 | 0 203 | 0 204 | 0 205 | -1 206 | -1 207 | 1 208 | -1 209 | -1 210 | 0 211 | 0 212 | 0 213 | -2 214 | -1 215 | 0 216 | 0 217 | -1 218 | -1 219 | 0 220 | 0 221 | -1 222 | -1 223 | -1 224 | -1 225 | 0 226 | 0 227 | 0 228 | 0 229 | 1 230 | -1 231 | -1 232 | -1 233 | -1 234 | 0 235 | -1 236 | -1 237 | 0 238 | 0 239 | -1 240 | -1 241 | 0 242 | 0 243 | -1 244 | 0 245 | 0 246 | 0 247 | -1 248 | 0 249 | -1 250 | -1 251 | -1 252 | 0 253 | -1 254 | 0 255 | 0 256 | 0 257 | 0 258 | 0 259 | 1 260 | 0 261 | -1 262 | 0 263 | 1 264 | 0 265 | -1 266 | -1 267 | -1 268 | 0 269 | 0 270 | 0 271 | 0 272 | -2 273 | -1 274 | -1 275 | 0 276 | 0 277 | 0 278 | -1 279 | 0 280 | 0 281 | 0 282 | 1 283 | -1 284 | 0 285 | 0 286 | 0 287 | -2 288 | -1 289 | -1 290 | 0 291 | 0 292 | 0 293 | 0 294 | -1 295 | 0 296 | 0 297 | 0 298 | 0 299 | 0 300 | 0 301 | 1 302 | -1 303 | -1 304 | -1 305 | -1 306 | 0 307 | 0 308 | 1 309 | 0 310 | 0 311 | 0 312 | -1 313 | 1 314 | -1 315 | 0 316 | 0 317 | 0 318 | 0 319 | -1 320 | 0 321 | 0 322 | 0 323 | -1 324 | 0 325 | -1 326 | -1 327 | -1 328 | -1 329 | 0 330 | 0 331 | 0 332 | 0 333 | 0 334 | -1 335 | -1 336 | 0 337 | 0 338 | 0 339 | -1 340 | 0 341 | 0 342 | -1 343 | -1 344 | 1 345 | -1 346 | 0 347 | 1 348 | 0 349 | 0 350 | 0 351 | 0 352 | 0 353 | -1 354 | 0 355 | 0 356 | 0 357 | 1 358 | 0 359 | 0 360 | 0 361 | 0 362 | 1 363 | 0 364 | -1 365 | 0 366 | 1 367 | 0 368 | 0 369 | 0 370 | -1 371 | -1 372 | 0 373 | -1 374 | -1 375 | -1 376 | 0 377 | 0 378 | 1 379 | 0 380 | -1 381 | 0 382 | 0 383 | 0 384 | 0 385 | -1 386 | 0 387 | -1 388 | 0 389 | 0 390 | 0 391 | -1 392 | 0 393 | -1 394 | -1 395 | -2 396 | 0 397 | 0 398 | 0 399 | 0 400 | 0 401 | 0 402 | -1 403 | 0 404 | 0 405 | -1 406 | 0 407 | 0 408 | 0 409 | -1 410 | 0 411 | 1 412 | 0 413 | 0 414 | 1 415 | 0 416 | 0 417 | 0 418 | 0 419 | 1 420 | -1 421 | 0 422 | 0 423 | -1 424 | 0 425 | -1 426 | 0 427 | -1 428 | 0 429 | 0 430 | 1 431 | 0 432 | 0 433 | 1 434 | -1 435 | -1 436 | -1 437 | 0 438 | 0 439 | -1 440 | 0 441 | 1 442 | 0 443 | -1 444 | 0 445 | -1 446 | 1 447 | 0 448 | 0 449 | 0 450 | 0 451 | 0 452 | 0 453 | -1 454 | -1 455 | 0 456 | 0 457 | 0 458 | 0 459 | -1 460 | 0 461 | 0 462 | -1 463 | -2 464 | -1 465 | -1 466 | 0 467 | -1 468 | 1 469 | 0 470 | 0 471 | -1 472 | -1 473 | 0 474 | 0 475 | -1 476 | 1 477 | 0 478 | 0 479 | 1 480 | 1 481 | -1 482 | 0 483 | -1 484 | 0 485 | 0 486 | -1 487 | -1 488 | -1 489 | -1 490 | 0 491 | 0 492 | 0 493 | 0 494 | 0 495 | 0 496 | 0 497 | 0 498 | -1 499 | 0 500 | 0 501 | 0 502 | -1 503 | 0 504 | 0 505 | 0 506 | 0 507 | 0 508 | -1 509 | 0 510 | 0 511 | 1 512 | -1 513 | -------------------------------------------------------------------------------- /nn_data/MNIST_1_6/layers/L1_dense_B.csv: -------------------------------------------------------------------------------- 1 | 0 2 | -1 3 | -1 4 | 0 5 | 0 6 | 1 7 | 0 8 | 2 9 | 0 10 | 0 11 | -1 12 | -1 13 | 0 14 | -1 15 | 1 16 | -1 17 | 0 18 | -1 19 | -1 20 | -1 21 | -1 22 | 1 23 | -1 24 | 1 25 | 0 26 | -1 27 | 0 28 | -1 29 | 0 30 | 0 31 | -2 32 | 1 33 | 0 34 | 1 35 | 0 36 | 0 37 | 1 38 | 1 39 | 0 40 | 1 41 | -1 42 | -1 43 | -1 44 | -1 45 | -1 46 | 0 47 | -1 48 | 1 49 | 1 50 | -1 51 | 1 52 | -1 53 | -1 54 | -1 55 | 0 56 | 0 57 | 1 58 | 0 59 | -1 60 | -1 61 | -1 62 | 0 63 | 1 64 | 0 65 | 0 66 | 0 67 | -1 68 | 0 69 | -1 70 | 0 71 | 1 72 | 0 73 | 0 74 | 0 75 | 0 76 | 0 77 | -1 78 | 0 79 | -1 80 | 0 81 | -1 82 | 0 83 | -1 84 | 0 85 | 1 86 | -1 87 | 0 88 | -1 89 | -1 90 | -1 91 | -1 92 | 0 93 | 0 94 | -1 95 | 1 96 | 0 97 | 2 98 | 1 99 | 0 100 | 1 101 | 0 102 | -1 103 | -1 104 | 1 105 | 1 106 | 0 107 | 1 108 | 1 109 | -1 110 | 1 111 | 0 112 | 0 113 | -1 114 | -1 115 | 0 116 | 1 117 | 0 118 | 1 119 | 1 120 | 1 121 | -1 122 | 0 123 | -1 124 | -1 125 | 0 126 | 1 127 | 0 128 | 0 129 | -1 130 | 1 131 | -1 132 | 0 133 | 0 134 | 0 135 | 2 136 | 1 137 | 0 138 | 0 139 | 0 140 | 0 141 | 0 142 | 0 143 | 1 144 | 1 145 | 0 146 | 0 147 | 0 148 | 1 149 | 1 150 | 0 151 | 0 152 | 0 153 | 0 154 | 0 155 | -1 156 | -1 157 | 0 158 | 0 159 | 0 160 | -1 161 | 1 162 | 0 163 | -1 164 | 0 165 | 0 166 | -1 167 | -1 168 | 0 169 | -1 170 | 0 171 | -1 172 | 0 173 | 0 174 | 1 175 | -1 176 | 0 177 | 1 178 | 0 179 | 1 180 | -1 181 | 0 182 | -1 183 | -1 184 | 0 185 | 0 186 | -1 187 | -1 188 | 1 189 | 1 190 | 0 191 | 1 192 | 0 193 | 0 194 | 0 195 | 1 196 | -1 197 | 0 198 | 0 199 | 0 200 | 0 201 | 1 202 | 0 203 | 0 204 | 0 205 | 1 206 | 1 207 | 0 208 | -1 209 | 0 210 | 0 211 | 0 212 | 0 213 | -1 214 | -1 215 | -1 216 | 0 217 | -1 218 | 0 219 | 1 220 | -1 221 | 0 222 | 1 223 | 0 224 | 0 225 | 0 226 | 0 227 | 0 228 | 0 229 | 0 230 | 0 231 | 0 232 | 0 233 | -1 234 | 0 235 | 0 236 | 0 237 | 0 238 | -1 239 | -1 240 | 1 241 | -1 242 | 0 243 | 0 244 | 0 245 | 1 246 | 0 247 | 0 248 | 0 249 | 0 250 | -1 251 | 0 252 | 0 253 | 0 254 | -1 255 | 0 256 | 1 257 | 0 258 | -1 259 | -1 260 | 0 261 | -1 262 | 0 263 | 0 264 | 0 265 | 0 266 | 0 267 | 0 268 | 0 269 | 0 270 | 1 271 | 0 272 | 1 273 | 1 274 | -1 275 | 0 276 | 1 277 | -1 278 | -1 279 | 1 280 | 0 281 | 1 282 | 0 283 | 0 284 | 0 285 | 0 286 | 0 287 | 0 288 | 0 289 | 0 290 | 0 291 | 1 292 | 0 293 | -1 294 | 0 295 | -1 296 | 0 297 | 0 298 | 1 299 | 0 300 | 0 301 | 0 302 | 1 303 | 0 304 | 0 305 | 0 306 | 0 307 | -1 308 | 0 309 | -1 310 | 1 311 | 0 312 | 1 313 | 0 314 | -1 315 | -1 316 | 0 317 | 0 318 | 0 319 | 0 320 | 1 321 | 0 322 | -1 323 | 0 324 | 1 325 | 1 326 | -1 327 | 0 328 | 0 329 | 1 330 | 1 331 | 0 332 | 1 333 | 0 334 | 0 335 | -1 336 | -1 337 | 0 338 | -1 339 | -1 340 | -1 341 | 0 342 | -1 343 | 0 344 | 0 345 | 0 346 | 1 347 | 0 348 | 1 349 | 0 350 | 0 351 | 1 352 | 0 353 | 1 354 | 1 355 | -1 356 | -1 357 | 0 358 | 0 359 | 0 360 | 0 361 | -1 362 | 1 363 | 1 364 | 1 365 | 0 366 | 0 367 | 1 368 | 0 369 | -1 370 | 0 371 | 0 372 | -1 373 | -1 374 | 1 375 | 0 376 | 1 377 | 0 378 | -1 379 | 0 380 | 0 381 | 0 382 | -1 383 | 0 384 | 0 385 | -1 386 | 0 387 | 1 388 | 0 389 | 1 390 | -1 391 | 0 392 | 0 393 | 1 394 | 0 395 | 0 396 | 0 397 | 0 398 | 0 399 | 0 400 | -1 401 | 0 402 | 0 403 | 1 404 | -1 405 | -1 406 | 0 407 | -1 408 | 1 409 | 1 410 | 0 411 | -1 412 | 0 413 | -1 414 | 0 415 | 1 416 | 1 417 | 0 418 | 0 419 | 1 420 | -1 421 | 0 422 | 0 423 | -1 424 | 1 425 | -1 426 | -1 427 | 0 428 | -1 429 | 0 430 | 0 431 | 1 432 | 0 433 | 0 434 | -1 435 | 1 436 | -1 437 | 0 438 | 0 439 | 1 440 | 0 441 | 0 442 | 0 443 | 0 444 | -1 445 | -1 446 | 0 447 | -2 448 | 0 449 | 0 450 | 0 451 | 0 452 | -1 453 | 0 454 | 1 455 | -1 456 | 0 457 | 0 458 | -1 459 | 0 460 | 0 461 | 0 462 | 0 463 | 0 464 | 1 465 | 0 466 | 0 467 | 0 468 | 0 469 | 0 470 | -1 471 | 0 472 | 0 473 | -1 474 | 1 475 | 1 476 | 1 477 | 0 478 | -1 479 | 1 480 | 0 481 | -1 482 | 0 483 | 1 484 | 1 485 | 0 486 | 0 487 | 0 488 | 0 489 | -1 490 | 0 491 | 0 492 | -1 493 | 0 494 | 0 495 | 1 496 | 0 497 | 0 498 | 1 499 | 0 500 | -1 501 | -1 502 | -1 503 | 1 504 | -1 505 | -1 506 | 0 507 | -1 508 | -1 509 | -1 510 | 0 511 | -------------------------------------------------------------------------------- /nn_data/MNIST_1_6/layers/L2_dense_B.csv: -------------------------------------------------------------------------------- 1 | 0 2 | -1 3 | 0 4 | 0 5 | 0 6 | 0 7 | -1 8 | -1 9 | 2 10 | 0 11 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "conv2d", 4 | "filters": 4, 5 | "kernel_size": [ 6 | 6, 7 | 6 8 | ], 9 | "strides": [ 10 | 1, 11 | 1 12 | ], 13 | "padding": "valid", 14 | "channels": 1, 15 | "weights": "layers/L0_conv2d_W.csv", 16 | "bias": "layers/L0_conv2d_B.csv", 17 | "activation": "relu_modulo", 18 | "modulus": 1024, 19 | "scale": 1, 20 | "scale_inv": 0 21 | }, 22 | { 23 | "name": "avg_pool2d", 24 | "pool_size": [ 25 | 2, 26 | 2 27 | ], 28 | "strides": [ 29 | 2, 30 | 2 31 | ], 32 | "padding": "valid", 33 | "fused": false, 34 | "modulus": 1024, 35 | "scale": 4, 36 | "scale_inv": 0 37 | }, 38 | { 39 | "name": "conv2d", 40 | "filters": 16, 41 | "kernel_size": [ 42 | 6, 43 | 6 44 | ], 45 | "strides": [ 46 | 1, 47 | 1 48 | ], 49 | "padding": "valid", 50 | "channels": 4, 51 | "weights": "layers/L2_conv2d_W.csv", 52 | "bias": "layers/L2_conv2d_B.csv", 53 | "activation": "relu_modulo", 54 | "modulus": 1024, 55 | "scale": 4, 56 | "scale_inv": 0 57 | }, 58 | { 59 | "name": "avg_pool2d", 60 | "pool_size": [ 61 | 2, 62 | 2 63 | ], 64 | "strides": [ 65 | 2, 66 | 2 67 | ], 68 | "padding": "valid", 69 | "fused": false, 70 | "modulus": 1024, 71 | "scale": 4, 72 | "scale_inv": 0 73 | }, 74 | { 75 | "name": "conv2d", 76 | "filters": 16, 77 | "kernel_size": [ 78 | 3, 79 | 3 80 | ], 81 | "strides": [ 82 | 1, 83 | 1 84 | ], 85 | "padding": "valid", 86 | "channels": 16, 87 | "weights": "layers/L4_conv2d_W.csv", 88 | "bias": "layers/L4_conv2d_B.csv", 89 | "activation": "relu_modulo", 90 | "modulus": 1024, 91 | "scale": 4, 92 | "scale_inv": 0 93 | }, 94 | { 95 | "name": "flatten", 96 | "modulus": 1024, 97 | "scale": 4, 98 | "scale_inv": 0 99 | }, 100 | { 101 | "name": "dense", 102 | "activation": "relu_modulo", 103 | "rows": 16, 104 | "columns": 64, 105 | "weights": "layers/L6_dense_W.csv", 106 | "bias": "layers/L6_dense_B.csv", 107 | "modulus": 1024, 108 | "scale": 4, 109 | "scale_inv": 0 110 | }, 111 | { 112 | "name": "dense", 113 | "activation": "softmax_modulo", 114 | "rows": 64, 115 | "columns": 10, 116 | "weights": "layers/L7_dense_W.csv", 117 | "bias": "layers/L7_dense_B.csv", 118 | "modulus": 1024, 119 | "scale": 4, 120 | "scale_inv": 0 121 | } 122 | ] -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/dataset/mnist_100_labels.csv: -------------------------------------------------------------------------------- 1 | 0 2 | 4 3 | 2 4 | 9 5 | 7 6 | 8 7 | 1 8 | 4 9 | 3 10 | 1 11 | 1 12 | 3 13 | 0 14 | 2 15 | 1 16 | 5 17 | 6 18 | 8 19 | 3 20 | 8 21 | 2 22 | 7 23 | 1 24 | 0 25 | 9 26 | 1 27 | 8 28 | 6 29 | 1 30 | 1 31 | 3 32 | 9 33 | 7 34 | 4 35 | 9 36 | 0 37 | 0 38 | 0 39 | 6 40 | 3 41 | 5 42 | 9 43 | 5 44 | 9 45 | 9 46 | 3 47 | 9 48 | 4 49 | 0 50 | 8 51 | 6 52 | 4 53 | 0 54 | 1 55 | 1 56 | 0 57 | 7 58 | 0 59 | 6 60 | 7 61 | 4 62 | 1 63 | 8 64 | 0 65 | 0 66 | 9 67 | 2 68 | 6 69 | 4 70 | 7 71 | 7 72 | 0 73 | 7 74 | 7 75 | 4 76 | 8 77 | 5 78 | 3 79 | 4 80 | 9 81 | 6 82 | 7 83 | 3 84 | 9 85 | 7 86 | 6 87 | 5 88 | 8 89 | 9 90 | 2 91 | 0 92 | 2 93 | 1 94 | 2 95 | 6 96 | 3 97 | 6 98 | 3 99 | 9 100 | 7 101 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L0_conv2d_B.csv: -------------------------------------------------------------------------------- 1 | 0 2 | 0 3 | 1 4 | 0 5 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L0_conv2d_W.csv: -------------------------------------------------------------------------------- 1 | 0,-1,0,0,1,-1,-1,-1,1,3,2,1,-1,-1,1,2,2,2,-1,-1,0,2,1,1,-1,0,0,0,0,0,-1,-1,-1,0,1,-1 2 | -1,0,0,-1,-2,-2,-1,0,0,0,0,0,-1,0,1,1,2,1,0,1,0,1,1,1,1,1,0,0,-1,1,0,-1,0,-2,0,0 3 | 0,0,0,0,0,-1,0,0,1,2,2,1,1,1,1,2,2,2,1,1,1,0,0,1,0,-1,-3,-3,-2,-1,-1,-2,-2,-2,-2,-1 4 | 0,0,-1,-1,0,0,-1,-1,0,0,-1,-1,-1,0,0,1,0,0,1,1,1,1,1,-1,1,1,1,2,0,0,0,1,1,1,0,0 5 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L2_conv2d_B.csv: -------------------------------------------------------------------------------- 1 | -2 2 | 2 3 | 1 4 | 3 5 | -2 6 | 1 7 | 1 8 | 1 9 | -1 10 | -1 11 | 2 12 | 3 13 | 2 14 | -2 15 | 0 16 | -3 17 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L2_conv2d_W.csv: -------------------------------------------------------------------------------- 1 | 0,0,0,0,-1,0,0,0,0,0,0,0,0,0,1,0,0,0,-1,-1,-1,-1,0,0,-1,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,-1,-2,-2,-1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,1,1,1,0,0,1,1,1,0,0,1,-1,-1,0,1,1,0,0,0,1,1,0,0,0,-1,-1,0,0,0,-1,-1,0,0,0,0,-1,-2,-2,-1,0,0,-1,-1,-1,-1,0,0,0,0,0,0,0,0,1,1,1,1,1,0 2 | 0,0,0,0,0,0,0,0,0,1,0,-1,0,0,0,1,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,-1,-1,0,0,0,0,-1,-1,-1,-1,0,1,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0 3 | 0,0,0,0,0,0,0,-1,1,1,0,-1,0,-1,1,1,-1,0,-1,0,1,0,-1,1,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,-1,0,1,0,0,0,0,1,1,0,0,0,-1,0,1,0,0,0,-1,0,0,0,0,0,0,0,0,1,0,-1,-1,-2,-1,1,1,-1,-1,-2,-1,1,0,0,0,-1,0,1,0,1,0,-1,0,0,-1,0,-1,-1,0,0,0,0,0,0,0,0,0,0,-1,0,1,0,0,0,-1,0,1,0,-1,0,0,0,0,-1,-1,0,0,1,0,-1,0,0,0,0,0,0,0,0,0,0,1,0,0 4 | 0,0,0,-1,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,-1,-2,-2,-1,1,1,1,1,-1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,-1,-3,-1,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,-1,0,1,1,1,1,0,1,0,0,1,1,0,0,-1,-2,-2,-1,0,0,1,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,1,0,-1,-1,1,1,1,0,-1,0,-1,-1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0 5 | 0,0,0,0,0,1,0,0,-1,-1,0,-1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,-1,0,0,0,0,-1,0,-1,-1,0,1,1,-1,0,-1,-1,-1,0,0,0,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,1,1,0,-1,0,0,0,0,-1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,-1,-1,0,0,0,-1,0,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,0,0 6 | 0,-1,-1,0,0,0,0,0,0,0,-1,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,1,1,0,-1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,-1,-1,0,0,0,-1,0,0,0,0,1,-1,-1,-1,0,0,1,1,0,-3,-1,-1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,-1,-1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1 7 | -1,-1,0,1,1,0,0,0,-1,0,0,0,0,0,-1,0,0,0,1,1,0,-1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,-1,1,0,0,-1,0,0,0,0,0,0,-1,0,0,0,0,0,-1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,-1,0,0,-1,-1,0,0,0,-1,0,-1,0,0,-1,-1,-1,0,0,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,-1,0,0,1,0,-1,0,0,0,0,0,0,0,0,1,0,0,0,0,0 8 | 0,-1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,-1,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,-1,0,0,0,0,1,0,0,1,0,1,1,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,-1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,-1,0 9 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,1,0,-1,0,-1,0,1,1,-3,-1,0,1,1,0,-2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,-1,-2,-1,0,1,1,-1,-3,0,0,0,0,-1,1,0,0,0,0,-1,0,1,1,0,0,0,0,1,0,-1,0,1,0,0,-1,0,1,0,-1,-1,0,0,0,0,-1,-1,0,0,0,0,0,-1,0,0,0,0,0,-1,1,1,0,-1,-1,-1,0,1,0,-1,-2,-1,0,1,0,-1,-2,-1,0,0,0,-2,-2 10 | -1,0,0,0,1,1,1,0,-1,0,1,1,1,-1,-1,0,0,0,0,-2,0,1,0,0,-1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,1,0,-1,-1,0,0,1,-1,-1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,-1,0,0,0,1,-1,0,1,0,1,0,-2,1,1,1,0,-2,0,1,0,-1,-1,0,0,0,-1,0,0,0,0,0,0,0,0,0,-1,0,1,0,0,-1,-1,1,1,0,-1,-1,0,0,0,-1,-1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0 11 | 0,0,0,0,0,-1,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,1,0,0,0,1,1,1,-1,0,0,0,0,0,0,0,0,0,-1,0,-1,-1,-1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1,-1,0,-1,0,0,0,0,0,0,-1,0,0,0,1,0,-1,0,1,0,0,0,-1,1,1,0,0,-1,-1,-1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,-1,0,0,0,0,0 12 | 0,-1,-1,-1,0,0,1,1,0,1,0,1,-1,0,0,0,0,0,-2,-2,-1,-1,-1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,-1,-1,0,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,-1,0,0,0,0,-1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,-1,-2,-2,-1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 13 | 0,0,0,0,0,-1,0,0,0,0,-1,-1,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,-1,-1,0,0,0,-1,-1,0,0,0,-1,0,1,0,0,0,-1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,-1,-1,0,0,0,-1,-2,0,1,1,-2,-3,0,1,1,0,-2,-1,0,1,0,0,-1,0,0,0,0,1,0,0,0,0,0,1,0,-1,-1,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 14 | 0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,-1,0,0,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,-1,0,1,0,0,0,-1,0,-1,0,1,0,-1,0,0,0,1,0,-1,0,0,1,0,-2,-1,1,0,0,-2,0,1,0,0,0,0,1,1,0,0,0,1,0,-1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,-1,0,0,0,1,0,-1,0,1,1,0,-1,0,0,0,0,-1,-1 15 | 0,0,0,0,-1,-1,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,1,0,0,0,-1,0,0,1,0,-1,-2,-1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,-1,0,0,0,0,0,-1,0,1,1,0,-1,-1,-2,-1,1,0,0,0,0,0,0,1,0,0,-1,0,0,1,1,0,-1,-1,-1,0,1,0,0,0,-1,0,1,0,0,1,1,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,0,0,0,-1,-1,0,1,0,0,-1,-1,-1,0,0,0,0,0,-1,-1,0,0 16 | 0,0,0,0,-1,0,0,0,0,0,0,-1,0,-1,0,0,1,0,-1,0,0,1,0,0,0,-1,0,1,0,0,0,0,0,0,1,-1,0,0,0,0,0,-1,0,0,0,1,1,-1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,0,-1,0,0,1,1,0,-1,0,0,0,0,0,-1,0,0,0,-1,-1,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,0,-1,0,0,0 17 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L4_conv2d_B.csv: -------------------------------------------------------------------------------- 1 | 1 2 | -2 3 | 0 4 | 1 5 | 1 6 | -1 7 | 0 8 | 3 9 | -1 10 | -1 11 | 2 12 | -1 13 | 1 14 | 3 15 | 1 16 | -1 17 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L4_conv2d_W.csv: -------------------------------------------------------------------------------- 1 | 1,1,-1,-1,1,1,0,1,0,0,0,-1,0,0,0,1,1,1,1,0,0,0,1,2,1,-1,0,1,0,-2,0,0,-2,0,1,0,0,0,-1,-1,-2,-1,1,0,1,0,0,0,0,0,-1,0,0,0,0,0,1,-1,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,-1,-2,-1,0,0,0,0,-1,0,0,0,1,0,-1,0,0,0,1,0,0,1,1,0,-1,1,1,-1,-1,0,0,0,1,-1,1,-1,-1,0,1,0,1,0,-1,0,1,0,0,0,1,0,-1,1,-2,-2,0,0,0,1,0,0,1,0,0,0,0,-1,-1 2 | 1,1,0,1,0,0,0,-1,0,0,0,0,-1,0,0,0,0,0,1,-1,0,1,0,0,1,-1,1,0,0,-1,0,0,0,0,0,1,1,0,0,0,0,1,-1,0,0,0,1,0,0,1,0,0,1,1,-1,-1,0,0,-1,1,-1,0,1,0,1,0,0,0,-1,0,0,-1,1,1,0,0,1,0,0,0,0,0,0,-1,0,1,1,0,1,0,0,-1,-1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,-1,0,0,0,1,1,0,1,0,0,1,0,0,0,0,-1,0,0,0,-2,-1,0,0,1,1,0,1,0,0,0,0,1,0,-1 3 | 0,1,0,0,-1,1,0,0,-1,1,0,0,1,0,0,0,-1,-1,1,1,2,0,1,0,1,0,0,-1,-1,-1,0,0,1,0,0,0,-1,-1,0,0,0,0,0,0,-1,0,0,0,1,0,0,1,0,0,0,1,1,0,1,-1,-1,-1,-2,0,-1,0,0,1,0,0,0,0,1,1,0,0,1,-1,1,1,0,0,0,2,0,-1,1,1,0,1,0,0,0,0,0,0,-1,0,-1,-2,-2,-2,-1,-1,1,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,-1,1,0,-1,0,0,0,0,0,0,0,1,0,0,0,-1,0,0,0,0,1,0 4 | -1,0,1,-1,1,-1,0,0,0,0,0,0,1,0,1,0,-1,0,1,1,1,0,0,0,0,0,2,0,0,1,1,1,0,0,-1,-1,-1,0,-1,1,0,0,1,1,0,0,0,-1,0,0,1,1,1,1,0,0,1,1,0,0,-1,0,-1,0,-1,0,0,0,-1,0,0,1,0,-1,0,-1,-1,0,0,1,1,1,0,0,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,-1,-1,0,0,-1,-1,-1,1,0,0,1,0,-1,-1,-1,0,0,1,1,0,1,-1,-1,0,0,1,1,-1,0,0 5 | -1,0,1,-2,1,1,-1,0,0,0,0,1,0,0,0,1,1,0,0,0,-1,0,-1,-1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,-1,0,0,0,0,1,1,-1,0,0,-1,0,1,0,0,0,-1,0,0,0,0,0,1,-1,1,0,0,-1,0,0,-1,0,1,1,0,1,0,-2,0,0,-2,-1,-1,0,1,1,0,1,1,0,0,-1,0,1,0,0,1,0,1,1,0,0,-1,0,0,1,0,0,0,-1,0,0,-1,0,-1,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,-1,0,1,0,0,0,0,1 6 | 1,0,0,1,0,0,2,1,0,1,0,0,0,0,1,0,0,-1,-1,1,2,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,-1,0,0,0,-1,0,-1,0,1,-1,-1,0,0,-1,0,1,0,0,0,0,0,0,1,-1,0,0,0,0,-1,0,0,1,0,0,1,0,2,1,2,0,0,2,0,1,-1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,0,0,1,0,-1,0,0,-2,0,1,0,0,-1,0,0,0,0,0,0,0,-1,0,1,1,0,0,-1,1,0,0,0,0,0,0,-1,1,1 7 | 1,0,0,1,0,0,-1,0,1,0,1,0,0,0,0,0,1,0,0,1,1,0,1,0,1,0,-1,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,1,0,-1,-1,0,1,0,0,1,1,1,0,0,0,0,0,0,0,2,0,0,-1,0,0,-1,0,0,0,-1,2,0,1,0,0,-1,-1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,1,0,0,1,1,2,1,0,0,0,0,1,1,0,0,0,0,0,0,0,-1,-1,1,0,1,1,0,-1,-1,0,0,-1,1,0,0 8 | -1,-1,1,0,-1,0,0,0,0,1,0,0,-1,0,-1,1,1,0,2,1,1,0,0,0,0,1,-1,-1,1,1,0,1,3,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,-1,0,0,0,0,0,-1,1,0,0,1,0,1,0,-1,1,0,0,1,0,0,0,-1,-1,-1,1,0,0,0,0,-1,0,0,1,-1,-1,0,0,0,-1,0,0,1,0,0,0,1,-1,0,-2,0,0,-1,0,1,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,0,0,1,0,0,1,-1,0,0,0,-2,1,0,-2,0,0,0,1,0,0 9 | 0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,1,1,-1,0,0,-2,1,1,1,0,0,1,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,-1,0,0,0,1,0,0,1,0,0,-1,0,-1,0,-1,-1,0,1,0,0,0,-1,0,0,1,0,0,-1,-1,-1,-1,-1,0,0,0,-1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,-1,-1,-1,-1,0,0,-1,0,0,1,1,0,0,1,0,0,0,1,0,-1,0,-1,-1,0,0,0,0,0 10 | 1,1,0,1,0,0,0,0,-1,1,0,0,0,0,1,0,-2,-2,0,0,0,0,-1,1,0,0,1,2,1,0,-1,1,0,-1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,2,1,0,0,0,0,0,0,0,0,0,1,-1,0,-1,-1,0,0,1,1,0,0,1,0,2,0,1,1,0,-1,1,1,0,-1,-2,0,1,0,2,0,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,-1,0,1,0,0,0,0,-1,0,-1,0,0,-1,0,0,0,0,1,0,0,0,0,0,0 11 | 0,0,0,-2,0,0,-2,0,0,0,-1,-1,0,0,0,1,1,0,0,1,0,1,0,0,1,0,-1,-1,0,0,-1,1,0,0,0,-1,0,0,1,0,0,-1,-1,0,-1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,-1,0,0,0,0,1,0,0,1,1,0,-1,1,-1,0,-1,-1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,-1,-1,-1,0,-1,0,0,-1,0,0,0,1,1,1,0,0,0,0,0,0,-1,-1,0,0,1,0,0,0,-2,-1,0,-1,0,0,1,1,-1,-1,0,0,1,0,0,0,0,-1 12 | 1,0,0,0,-1,0,1,1,0,-1,0,1,1,1,0,0,0,0,-1,0,1,0,0,0,-1,0,-1,0,0,0,0,1,1,1,0,-1,0,0,-1,0,0,-1,0,1,0,-1,-1,0,0,0,0,0,0,1,-1,0,0,0,0,-1,1,0,0,0,0,0,0,0,0,-1,0,0,-1,0,0,0,0,0,-1,0,1,0,0,0,1,0,-1,0,-1,-1,0,0,0,0,0,1,0,0,0,1,1,0,1,0,0,1,0,0,-1,0,0,1,0,-1,0,-1,0,0,0,1,-1,-1,0,0,0,1,0,0,1,1,1,1,0,-1,1,1,0,0,1,0,0,-1,0,1 13 | 1,1,-1,1,0,1,-1,1,0,-1,0,1,0,0,0,1,0,0,-2,-2,-3,0,0,0,0,-1,0,0,0,0,0,0,1,0,1,-1,1,0,1,0,0,-1,1,0,-1,0,0,1,0,0,1,0,0,0,0,0,0,0,-1,1,1,0,0,0,1,1,-1,0,0,0,0,1,0,0,1,1,0,1,-1,-1,0,0,1,-2,0,1,-1,0,0,0,0,0,0,0,0,0,0,-1,0,2,1,1,1,1,0,0,0,0,1,0,0,0,0,1,-1,-1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,1,0,0,-1,0,0,1,0,0,-1,0,-1,0 14 | -2,0,-1,0,0,1,0,-1,0,0,-1,0,0,0,0,0,0,0,0,0,-1,1,0,0,-1,0,1,0,1,0,0,1,1,0,-1,-1,0,1,0,0,0,1,0,-1,-1,0,0,1,1,-1,0,1,-1,-1,0,0,0,1,0,0,0,0,-1,0,1,1,0,0,0,0,0,-1,-1,0,0,0,1,1,1,0,1,0,0,-1,0,-1,1,0,-1,0,1,1,0,0,0,-1,0,0,0,-1,0,1,0,0,0,0,1,0,1,0,0,-1,0,-1,0,1,0,-1,0,1,1,0,0,0,-1,-1,1,1,0,1,0,0,0,-1,-1,0,1,0,-1,0,1,0,0,0 15 | 0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,-1,0,0,-1,-1,2,-1,-1,0,0,1,1,1,-1,-2,1,0,-1,0,1,0,0,0,-1,1,0,1,0,-1,1,0,-1,1,1,1,-1,1,0,0,0,0,0,0,1,0,0,0,-1,0,0,0,1,1,0,0,0,0,-1,0,1,-1,0,0,1,2,0,0,0,1,1,0,2,1,0,-1,0,0,-1,0,0,0,0,1,0,1,0,-1,0,-1,0,0,1,1,0,0,0,0,-1,0,0,0,0,1,2,1,1,1,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,1,0,0,-1,0,1,1 16 | 1,0,0,2,0,-1,2,0,0,0,0,-1,1,0,0,0,0,0,0,0,1,0,0,-1,0,0,0,0,-1,0,0,-1,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,-1,-1,0,0,-1,0,1,-1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,-1,1,0,1,2,1,0,1,0,0,0,0,0,1,0,-1,-1,-1,1,0,-1,0,0,0,0,0,1,0,1,1,2,1,0,0,1,0,-1,-1,0,0,1,1,0,0,1,1,1,-1,0,-1,0,0,0,1,0,0,-1,0,1,0,0,-1,0,0,0,0,-1,0,0,-1,0,0,0 17 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L6_dense_B.csv: -------------------------------------------------------------------------------- 1 | 1 2 | 1 3 | 1 4 | 1 5 | 1 6 | 0 7 | 3 8 | -2 9 | 0 10 | 2 11 | 1 12 | 1 13 | -1 14 | 3 15 | 0 16 | 0 17 | 0 18 | 2 19 | 0 20 | 0 21 | 1 22 | 2 23 | 1 24 | 1 25 | 2 26 | -1 27 | 1 28 | 3 29 | 2 30 | 1 31 | 3 32 | 0 33 | 3 34 | 1 35 | 3 36 | 2 37 | 4 38 | 1 39 | 3 40 | -1 41 | 0 42 | 1 43 | -1 44 | -2 45 | 3 46 | 1 47 | 0 48 | 0 49 | 0 50 | 0 51 | 2 52 | -2 53 | 0 54 | -3 55 | 1 56 | 0 57 | -1 58 | -2 59 | 2 60 | 2 61 | 1 62 | -3 63 | 1 64 | -2 65 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L6_dense_W.csv: -------------------------------------------------------------------------------- 1 | 1,1,0,1,0,0,1,-1,0,1,2,0,0,-1,1,-1,1,0,1,1,-1,0,-1,1,2,1,0,-1,0,0,-1,0,0,0,1,0,-1,0,2,1,-2,0,1,-1,1,0,1,-1,1,-2,2,1,-1,-1,-2,-1,0,2,-1,-1,1,0,-1,1 2 | 0,1,1,1,1,1,1,1,0,-2,0,1,1,0,0,1,0,-1,-1,1,-1,0,0,0,0,1,-1,1,0,0,0,1,0,0,0,1,-1,1,-2,1,-1,1,0,1,0,2,-1,1,0,0,0,1,0,0,-1,0,0,1,-1,1,0,1,0,0 3 | 1,2,0,-1,1,-1,2,-1,0,1,2,0,-1,-1,1,-1,1,0,0,0,1,-1,1,0,0,-1,0,1,0,-1,-1,1,1,1,1,0,0,-1,0,-1,0,-1,-1,1,0,-1,0,1,0,-1,-1,2,-1,0,0,0,0,1,2,0,1,1,-2,0 4 | 1,-1,1,1,1,-1,0,0,0,0,0,1,0,0,0,-1,1,1,1,-1,-1,1,1,-1,1,1,0,0,1,0,0,-1,1,0,2,0,-1,-1,1,1,-1,0,0,0,-1,-1,0,0,0,0,0,1,0,0,-1,-1,1,0,0,-1,1,0,0,0 5 | 0,1,-1,0,0,1,-1,0,1,0,-1,0,1,-1,0,-1,-1,1,1,0,1,0,-1,1,1,2,-1,1,-1,1,1,0,0,1,1,1,-1,1,-1,0,0,-1,-1,1,1,0,1,-1,-1,0,0,0,-1,1,1,1,0,1,1,1,-1,0,1,0 6 | 2,-1,0,-1,2,-1,-1,1,1,1,-1,-1,2,1,0,-1,0,0,1,1,2,-1,-1,-1,-1,0,0,-1,-1,-1,-1,1,-1,0,-1,-1,-1,1,1,-1,0,-1,0,0,-1,-1,1,-1,-1,0,-1,1,-1,0,1,0,1,0,1,0,0,1,0,-1 7 | -1,1,-1,1,0,0,0,1,-1,0,1,0,0,1,2,-1,0,-1,0,-1,0,2,-1,-1,-1,0,1,0,2,0,0,1,-1,1,-1,2,0,1,-1,0,0,0,-1,1,1,2,1,0,-1,0,0,0,0,1,2,-1,0,1,0,2,0,1,-1,0 8 | 1,0,1,0,0,1,0,-1,-1,0,-1,0,0,1,0,0,1,1,0,-1,0,-1,0,1,1,-1,0,2,0,-1,0,-2,1,2,-1,0,0,0,0,-1,1,-1,0,0,0,1,0,1,0,1,1,-1,1,-1,1,-1,0,-2,1,0,0,-1,0,-2 9 | 0,1,1,-1,2,0,1,-1,0,1,1,1,-1,1,-1,1,2,1,1,-1,0,0,-1,-1,0,-1,-1,0,0,-1,0,0,1,1,1,-1,1,0,1,0,0,0,1,-1,0,-1,1,1,0,0,0,-1,-1,0,1,0,0,0,2,-1,1,-1,0,0 10 | 0,0,0,2,1,-1,1,0,-1,-1,0,0,1,-2,1,0,-2,0,0,0,-2,0,-1,0,0,-1,-1,0,-1,0,-1,-1,0,-1,-1,0,-1,-1,0,1,0,1,0,1,0,-1,0,2,0,1,0,1,1,0,0,-1,2,-1,0,0,2,1,2,1 11 | -1,1,0,1,0,0,1,-1,0,-2,0,0,-1,0,-1,-1,1,1,1,-1,-1,-1,0,-1,-3,0,-1,1,-1,0,1,1,0,0,2,0,0,-1,0,0,-1,-1,-1,-1,-1,0,1,-1,0,-1,-1,-1,-1,0,1,-1,1,1,1,-1,0,-1,0,1 12 | 0,-1,-1,-1,0,1,0,1,1,1,-1,1,1,1,1,0,1,0,0,-1,1,-1,1,-1,-1,1,1,-1,1,1,-1,-2,1,1,0,0,-1,0,-1,0,1,1,1,1,-1,0,-1,0,0,0,0,0,-1,0,1,1,-1,-2,-1,1,0,1,0,1 13 | -1,0,1,0,-2,1,1,0,2,0,1,0,0,0,-2,1,0,-1,-2,1,0,0,0,1,1,1,1,0,1,2,1,0,0,1,0,-1,0,0,0,1,1,0,-1,1,-1,1,1,0,0,1,1,0,0,1,0,1,1,1,-1,1,-1,-1,2,0 14 | 1,1,1,-1,1,-1,-1,0,0,0,0,-1,1,1,-1,0,0,0,-1,0,1,0,0,0,1,-1,0,0,-1,1,1,0,1,-1,0,1,1,1,1,1,-1,-1,-1,-1,1,0,0,0,1,1,0,-1,-1,-1,0,-1,0,-1,1,0,1,-1,1,1 15 | 1,0,0,0,0,1,1,-1,1,1,0,-1,1,-1,1,0,-2,0,0,0,-1,0,1,1,1,-1,-1,-1,0,0,0,0,0,0,-1,1,1,1,0,1,0,0,-1,0,1,1,-1,-1,1,0,-1,1,-1,0,-1,1,-1,0,1,-1,-1,0,1,1 16 | 1,0,0,-1,-1,1,-1,1,1,1,0,0,1,0,1,1,-1,-1,0,0,2,0,0,0,-1,-1,0,1,-1,-1,-1,0,-1,-1,-1,-2,0,-1,0,-1,1,0,0,0,0,0,-2,1,0,0,1,1,-1,0,1,0,1,0,-1,0,1,0,-1,0 17 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L7_dense_B.csv: -------------------------------------------------------------------------------- 1 | -2 2 | 1 3 | -3 4 | -3 5 | 0 6 | 1 7 | -1 8 | -1 9 | 3 10 | 2 11 | -------------------------------------------------------------------------------- /nn_data/MNIST_2_4_T1/layers/L7_dense_W.csv: -------------------------------------------------------------------------------- 1 | -1,0,-1,-1,1,0,-2,0,-1,1 2 | 0,1,0,-1,0,0,-1,-1,0,-2 3 | 1,0,0,0,-1,1,-1,1,0,1 4 | 0,-3,0,-2,0,-1,-1,0,-1,-1 5 | -2,0,-1,-3,0,-1,-1,1,-1,0 6 | 0,-1,0,2,-1,0,-1,-1,1,-2 7 | 2,2,-1,-2,-2,-1,-2,0,1,0 8 | -2,0,1,0,-1,0,-1,1,-1,-2 9 | 1,1,-1,2,0,0,-2,0,-1,1 10 | -6,1,-2,1,1,0,-4,-3,-1,1 11 | 1,1,-1,-3,-1,-1,-4,-1,-2,-2 12 | 0,-4,0,-2,-1,-1,1,-3,-3,-2 13 | -1,1,0,1,0,-1,-3,1,-1,1 14 | -1,-2,-6,-1,-4,2,1,-4,-1,0 15 | -2,1,0,-3,2,-1,-1,-2,-2,-4 16 | -3,-3,-2,0,-2,0,-2,-2,-1,-2 17 | 1,-2,-2,-2,-3,1,1,-4,-3,-1 18 | 0,-1,-1,-1,2,0,1,-3,0,0 19 | 0,1,-2,-1,1,-1,1,-1,-1,-2 20 | -1,-2,0,-1,-5,-3,-3,0,-3,-2 21 | -2,1,-2,0,-3,1,-4,-1,-2,1 22 | -3,-3,2,-2,0,-1,0,-2,-3,0 23 | -5,-2,-2,-3,1,-3,-3,-5,-5,-1 24 | 0,0,-1,1,1,-1,-2,-1,2,0 25 | 0,-3,1,-1,1,0,0,-1,0,2 26 | -1,-2,0,1,-1,-1,0,-2,-2,1 27 | -3,-2,1,1,0,1,-1,-2,1,-2 28 | 0,-2,0,-2,0,1,1,-1,0,-2 29 | -4,-3,2,-1,0,-1,-1,-3,1,-3 30 | 0,-2,0,1,-2,-1,-1,-1,0,1 31 | 0,-2,-1,0,-3,-2,1,-2,1,0 32 | -1,0,1,-3,-5,-3,-1,-3,-3,-5 33 | -1,-3,-1,-2,1,-1,0,-2,-1,0 34 | 1,-1,0,1,0,1,1,-3,1,-1 35 | 1,-4,-3,-2,0,-2,1,-2,-2,0 36 | -1,0,1,-2,1,-3,0,-1,1,-1 37 | -2,0,-6,-2,-2,1,-2,-1,2,2 38 | -1,1,0,0,0,-1,-1,0,1,1 39 | 1,-3,-1,-2,-1,0,-2,-1,-2,1 40 | 1,-1,0,0,0,-1,-2,1,0,0 41 | -2,-1,-1,1,-1,0,-1,-1,1,-1 42 | -2,-4,2,-4,-1,-4,-3,-2,-3,-3 43 | 0,0,0,0,0,-1,-1,0,0,0 44 | -2,-1,0,0,-2,0,0,0,0,-3 45 | -1,1,1,-2,1,-3,-2,0,1,0 46 | 1,1,1,0,-3,-1,0,-1,1,-3 47 | 1,-1,0,-2,-2,-1,1,-1,-1,-1 48 | 0,-2,0,0,-1,2,-1,1,0,-2 49 | 0,-1,0,0,-1,0,-1,-1,0,0 50 | -2,-2,-2,1,0,1,-3,1,0,1 51 | 1,0,0,0,-3,0,-2,-2,-1,-1 52 | 0,1,1,-1,1,-1,-1,1,-1,-1 53 | -1,0,0,-1,0,-1,-1,-1,-1,0 54 | -1,1,0,1,-2,-3,0,1,-2,-2 55 | -2,0,-2,0,-3,1,1,-1,0,-2 56 | -6,-2,-3,1,-4,-4,-7,-1,-2,0 57 | 1,-3,0,0,-2,0,0,1,-1,0 58 | 1,1,1,-1,-2,-2,1,-1,-3,-1 59 | -1,1,-3,-1,1,0,1,-1,1,0 60 | -2,0,1,0,-4,1,-1,-1,1,-1 61 | 0,0,0,-3,0,0,-1,1,-2,-1 62 | -4,1,0,0,1,-2,1,1,-1,-3 63 | 0,-3,-1,1,-2,-1,-2,1,0,1 64 | 1,0,-1,0,0,-4,-3,1,-1,0 65 | -------------------------------------------------------------------------------- /test/include/LUT.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 10.05.21. 3 | // 4 | 5 | #ifndef FBS_LUT_H 6 | #define FBS_LUT_H 7 | 8 | #include "fbscontext.h" 9 | #include 10 | 11 | bool run_lut_tests(std::vector& bits); 12 | 13 | class SwitchingKey { 14 | public: 15 | SwitchingKey(std::shared_ptr& params, fbscrypto::LWEPrivateKey& pt_sk, lbcrypto::NativePoly& sk_poly); 16 | 17 | std::shared_ptr keyswitch(fbscrypto::LWECiphertext& ct); 18 | 19 | std::shared_ptr functional_keyswitch(fbscrypto::LWECiphertext& ct); 20 | 21 | private: 22 | 23 | std::shared_ptr params; 24 | std::vector>> KSK; 25 | std::vector>> F_KSK; 26 | 27 | fbscrypto::LWEPrivateKey key; 28 | lbcrypto::NativePoly sk_poly; 29 | 30 | }; 31 | 32 | class VerticalLUT { 33 | 34 | public: 35 | 36 | VerticalLUT(std::shared_ptr& params, SwitchingKey& key, uint32_t target_bits, std::function&); 37 | 38 | shared_ptr evaluate(fbscrypto::FBSFHEContext& ctx, std::vector& bits); 39 | 40 | private: 41 | 42 | uint32_t rem_bits; 43 | SwitchingKey& key; 44 | std::vector polyPowers; 45 | std::shared_ptr params; 46 | std::function F; 47 | std::vector> LUT_entries; 48 | 49 | }; 50 | 51 | class HorizontalLUT { 52 | 53 | public: 54 | 55 | HorizontalLUT(std::shared_ptr& params, SwitchingKey& key, uint32_t target_bits, std::vector>&); 56 | 57 | std::shared_ptr evaluate(fbscrypto::FBSFHEContext &ctx, std::vector& bits); 58 | 59 | private: 60 | 61 | SwitchingKey& key; 62 | std::shared_ptr params; 63 | std::vector> V_F; 64 | std::vector> LUT_entries; 65 | 66 | }; 67 | 68 | #endif //FBS_LUT_H 69 | -------------------------------------------------------------------------------- /test/include/error.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 16.08.21. 3 | // 4 | 5 | #ifndef FBS_ERROR_H 6 | #define FBS_ERROR_H 7 | 8 | #include "fbscontext.h" 9 | 10 | void measure_error(uint32_t fbsparamset_idx, uint32_t mod_bits); 11 | 12 | void measure_all_errors(); 13 | 14 | #endif //FBS_ERROR_H 15 | -------------------------------------------------------------------------------- /test/include/functions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 09.05.21. 3 | // 4 | 5 | #ifndef FBS_FUNCTIONS_H 6 | #define FBS_FUNCTIONS_H 7 | 8 | #include "setup.h" 9 | 10 | bool benchmark_functions(); 11 | 12 | #endif //FBS_FUNCTIONS_H 13 | -------------------------------------------------------------------------------- /test/include/implementation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 07.05.21. 3 | // 4 | 5 | #ifndef FBS_IMPLEMENTATION_H 6 | #define FBS_IMPLEMENTATION_H 7 | 8 | #include "setup.h" 9 | 10 | void time_parameter_set(fbscrypto::FBSFHEPARAMSET set, std::string name); 11 | 12 | void time_binary_parameter_set(fbscrypto::FBSFHEPARAMSET set, std::string name); 13 | 14 | #endif //FBS_IMPLEMENTATION_H 15 | -------------------------------------------------------------------------------- /test/include/setup.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 07.05.21. 3 | // 4 | 5 | #ifndef FBS_SETUP_H 6 | #define FBS_SETUP_H 7 | 8 | #include "fbscontext.h" 9 | #include "lwe.h" 10 | 11 | struct CryptoData { 12 | 13 | CryptoData(fbscrypto::FBSFHEPARAMSET set) : ctx(), set(set) { 14 | ctx.GenerateFDFHEContext(set); 15 | key = ctx.KeyGen(); 16 | ctx.BTKeyGen(key); 17 | } 18 | 19 | fbscrypto::FBSFHEContext ctx; 20 | fbscrypto::LWEPrivateKey key; 21 | fbscrypto::FBSFHEPARAMSET set; 22 | 23 | }; 24 | 25 | static std::shared_ptr add_ciphertexts(const std::shared_ptr& a, 26 | const std::shared_ptr& b) { 27 | 28 | auto q = a->GetA().GetModulus(); 29 | auto n = a->GetA().GetLength(); 30 | 31 | auto v = NativeVector(n, q); 32 | for(uint32_t i = 0; i < n; i++) { 33 | v[i] = a->GetA(i).ModAddFast(b->GetA(i), q); 34 | } 35 | 36 | auto bnew = a->GetB().ModAddFast(b->GetB(), q); 37 | 38 | return std::make_shared(v, bnew); 39 | } 40 | 41 | static std::shared_ptr sub_ciphertexts(const std::shared_ptr& a, 42 | const std::shared_ptr& b) { 43 | 44 | auto q = a->GetA().GetModulus(); 45 | auto n = a->GetA().GetLength(); 46 | 47 | auto v = NativeVector(n, q); 48 | for(uint32_t i = 0; i < n; i++) { 49 | v[i] = a->GetA(i).ModSubFast(b->GetA(i), q); 50 | } 51 | 52 | auto bnew = a->GetB().ModSubFast(b->GetB(), q); 53 | 54 | return std::make_shared(v, bnew); 55 | 56 | } 57 | 58 | 59 | static std::shared_ptr scale_ciphertexts(const std::shared_ptr& a, 60 | uint32_t scale) { 61 | 62 | auto q = a->GetA().GetModulus(); 63 | auto n = a->GetA().GetLength(); 64 | 65 | auto v = NativeVector(n, q); 66 | auto mu = q.ComputeMu(); 67 | for(uint32_t i = 0; i < n; i++) { 68 | v[i] = a->GetA(i).ModMulFast(scale, q, mu); 69 | } 70 | 71 | auto bnew = a->GetB().ModMulFast(scale, q, mu); 72 | 73 | return std::make_shared(v, bnew); 74 | } 75 | 76 | #endif //FBS_SETUP_H 77 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "implementation.h" 3 | #include "functions.h" 4 | #include "setup.h" 5 | #include "LUT.h" 6 | #include "error.h" 7 | 8 | int main() { 9 | 10 | std::cout << "### TIMING FDBFB PARAMETER SETS ###" << std::endl; 11 | 12 | for(uint32_t i = 0; i < 6; i++) { 13 | time_parameter_set(fbscrypto::FBSFHEPARAMSET_LIST[i], fbscrypto::FBSFHEPARAMSET_NAMES[i]); 14 | } 15 | 16 | std::cout << "### TIMING BINARY PARAMETER SETS ###" << std::endl; 17 | 18 | for(uint32_t i = 6; i < 9; i++) { 19 | time_binary_parameter_set(fbscrypto::FBSFHEPARAMSET_LIST[i], fbscrypto::FBSFHEPARAMSET_NAMES[i]); 20 | } 21 | 22 | std::cout << std::endl << "### TIMING ADDITION AND SCALAR MULTIPLICATION ###" << std::endl; 23 | 24 | benchmark_functions(); 25 | 26 | std::cout << std::endl << "### TIMING LUT EVALUATION FOR DIFFERENT BIT SIZE ###" << std::endl; 27 | 28 | std::vector bits = {2 * 6, 2 * 7, 2 * 8, 2 * 9, 2 * 10, 2 * 11}; 29 | 30 | run_lut_tests(bits); 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /test/src/error.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 18.08.21. 3 | // 4 | #include "error.h" 5 | #include "setup.h" 6 | 7 | void measure_error(uint32_t idx, uint32_t mod_bits) { 8 | 9 | auto data = CryptoData(fbscrypto::FBSFHEPARAMSET_LIST[idx]); 10 | auto T = 1 << mod_bits; 11 | uint32_t n = 100; 12 | 13 | std::vector cts(n); 14 | 15 | auto msg = data.ctx.Encode(1, T, fbscrypto::CIPHERTEXT_STATE::FRESH); 16 | auto base_ct = data.ctx.Encrypt(data.key, msg); 17 | auto map = [T](uint64_t a) { return a; }; 18 | auto bootsmap = fbscrypto::BootstrapFunction({map}, T); 19 | 20 | #pragma omp parallel for 21 | for(uint32_t i = 0; i < n; i++){ 22 | cts[i] = data.ctx.FullDomainBootstrap(base_ct, bootsmap, fbscrypto::SKIP_STEP::KEYSWITCH); 23 | } 24 | 25 | auto accu = data.ctx.Encrypt(data.key, 0, fbscrypto::CIPHERTEXT_STATE::TRIVIAL_BEFORE_KEYSWITCH); 26 | for(uint32_t i = 0; i < n; i++) 27 | (*accu) += (*cts[i]); 28 | 29 | auto res = data.ctx.Finalize(accu, fbscrypto::KEYSWITCH); 30 | int64_t plaintext = 0; 31 | data.ctx.Decrypt(data.key, res, &plaintext); 32 | 33 | std::cout << plaintext << " " << msg * (n % T) << std::endl; 34 | 35 | } 36 | 37 | void measure_all_errors() { 38 | for(uint32_t i = 0; i < 6; i++) { 39 | std::cout << "TESTING SET " << fbscrypto::FBSFHEPARAMSET_LIST[i] << std::endl; 40 | for(uint32_t m = 6; m <= 11; m++) { 41 | std::cout << "MODULUS = " << (1 << m) << std::endl; 42 | measure_error(i, m); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /test/src/functions.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 09.05.21. 3 | // 4 | 5 | #include 6 | #include "functions.h" 7 | #include 8 | 9 | #define TICK std::chrono::high_resolution_clock::now() 10 | 11 | void time_addition_scalar_mul(fbscrypto::FBSFHEPARAMSET set, std::string name) { 12 | CryptoData data(set); 13 | 14 | fbscrypto::LWEPlaintext m1 = 5, m2 = 5; 15 | uint32_t rounds = 10; 16 | 17 | uint64_t total_sum = 0, total_scale = 0; 18 | 19 | for(uint32_t i = 0; i < rounds; i++) { 20 | 21 | auto ct1 = data.ctx.Encrypt(data.key, m1); 22 | auto ct2 = data.ctx.Encrypt(data.key, m2); 23 | 24 | auto start = TICK; 25 | 26 | auto sum = add_ciphertexts(ct1, ct2); 27 | 28 | auto step = TICK; 29 | 30 | auto scaled = scale_ciphertexts(ct1, 20); 31 | 32 | auto stop = TICK; 33 | 34 | auto sum_elapsed = std::chrono::duration_cast(step-start).count(); 35 | auto sclale_elapsed = std::chrono::duration_cast(stop-step).count(); 36 | total_sum += sum_elapsed; 37 | total_scale += sclale_elapsed; 38 | } 39 | 40 | std::cout << "Parameter set " << name << " took " << double(total_sum) / rounds << "us for addition and " 41 | << double(total_scale) / rounds << "us for scalar multiplication " << std::endl; 42 | 43 | } 44 | 45 | 46 | bool benchmark_functions() { 47 | for(uint32_t i = 0; i < 6; i++) { 48 | time_addition_scalar_mul(fbscrypto::FBSFHEPARAMSET_LIST[i], fbscrypto::FBSFHEPARAMSET_NAMES[i]); 49 | } 50 | 51 | return true; 52 | } 53 | -------------------------------------------------------------------------------- /test/src/implementation.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by leonard on 07.05.21. 3 | // 4 | 5 | #include "implementation.h" 6 | 7 | void time_parameter_set(fbscrypto::FBSFHEPARAMSET set, std::string name) { 8 | CryptoData data(set); 9 | uint32_t dummy = 42; 10 | int64_t pt; 11 | NativeInteger enc = data.ctx.Encode(dummy, 64, fbscrypto::FRESH); 12 | auto ct = data.ctx.Encrypt(data.key, enc.ConvertToInt()); 13 | 14 | auto start = std::chrono::high_resolution_clock::now(); 15 | 16 | auto bst = data.ctx.Bootstrap(ct); 17 | 18 | auto stop = std::chrono::high_resolution_clock::now(); 19 | auto elapsed = std::chrono::duration_cast(stop-start); 20 | 21 | data.ctx.Decrypt(data.key, bst, &pt); 22 | auto res = data.ctx.Decode(pt, 64); 23 | 24 | std::cerr << "[" << name << "]" << " Bootstrap took " << elapsed.count() << "ms" << std::endl; 25 | } 26 | 27 | void time_binary_parameter_set(fbscrypto::FBSFHEPARAMSET set, std::string name) { 28 | CryptoData data(set); 29 | uint32_t dummy = 1; 30 | auto ct = data.ctx.Encrypt(data.key, dummy); 31 | 32 | auto start = std::chrono::high_resolution_clock::now(); 33 | 34 | volatile auto bst = data.ctx.BootstrapBinary(ct); 35 | 36 | auto stop = std::chrono::high_resolution_clock::now(); 37 | auto elapsed = std::chrono::duration_cast(stop-start); 38 | 39 | std::cerr << "[" << name << "]" << " Bootstrap took " << elapsed.count() << "ms" << std::endl; 40 | } 41 | --------------------------------------------------------------------------------