From ba8cf904f5aee6b75f5e4dda241231bd6a0382a8 Mon Sep 17 00:00:00 2001 From: Jack Crawford Date: Wed, 1 Sep 2021 20:25:02 +0100 Subject: [PATCH 1/5] Experimental HEXL Acceleration Integration Experimental HEXL Acceleration Integration Co-authored-by: Hamish Hunt Co-authored-by: Flavio Bergamaschi Co-authored-by: Fabian Boemer --- CHANGES.md | 11 ++ CMakeLists.txt | 45 +++++- INSTALL.md | 63 ++++++-- VERSION | 2 +- benchmarks/CMakeLists.txt | 5 +- benchmarks/bench_common.h | 19 +++ benchmarks/bgv_basic.cpp | 257 ++++++++++++++++++++++++------- benchmarks/bgv_common.h | 24 ++- benchmarks/ckks_basic.cpp | 242 ++++++++++++++++++++--------- benchmarks/ckks_common.h | 9 +- benchmarks/fft_bench.cpp | 84 ++++++++++ include/helib/CModulus.h | 16 +- include/helib/Ctxt.h | 98 +++++------- include/helib/DoubleCRT.h | 95 ++++-------- include/helib/PAlgebra.h | 10 +- misc/algen/algen.py | 2 +- misc/algen/numth.py | 2 +- misc/format.sh | 13 +- misc/profiling/CMakeLists.txt | 36 +++++ misc/profiling/basic_circuit.cpp | 170 ++++++++++++++++++++ misc/psi/README.md | 24 +-- src/CMakeLists.txt | 23 +++ src/CModulus.cpp | 164 ++++++++++++-------- src/Context.cpp | 138 +++-------------- src/Ctxt.cpp | 16 +- src/DoubleCRT.cpp | 216 ++++++++++++++++++++++---- src/NumbTh.cpp | 7 +- src/PAlgebra.cpp | 19 ++- src/PrimeGenerator.h | 129 ++++++++++++++++ src/intelExt.cpp | 179 +++++++++++++++++++++ src/intelExt.h | 61 ++++++++ src/macro.h | 25 +++ src/matmul.cpp | 2 +- tests/CMakeLists.txt | 2 + tests/GTestGeneral.cpp | 2 + tests/TestHEXL.cpp | 247 +++++++++++++++++++++++++++++ tests/TestPermutations.cpp | 29 +++- tests/test_common.cpp | 2 +- 38 files changed, 1969 insertions(+), 519 deletions(-) create mode 100644 benchmarks/bench_common.h create mode 100644 benchmarks/fft_bench.cpp create mode 100644 misc/profiling/CMakeLists.txt create mode 100644 misc/profiling/basic_circuit.cpp create mode 100644 src/PrimeGenerator.h create mode 100644 src/intelExt.cpp create mode 100644 src/intelExt.h create mode 100644 src/macro.h create mode 100644 tests/TestHEXL.cpp diff --git a/CHANGES.md b/CHANGES.md index 81d276c62..46f941611 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,17 @@ Release Changes =============== +HElib 2.2.0, September 2021 +========================= +(tagged as v2.2.0) + +* Intel HEXL integration (experimental) + * HEXL NTT acceleration for m as a power of 2 + * HEXL acceleration to fundamental ciphertext operations + * Documentation update +* Update to benchmarks +* Bug fixes + HElib 2.1.0, March 2021 ========================= (tagged as v2.1.0) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84583faab..4ff8b8c7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,12 @@ # See the License for the specific language governing permissions and # limitations under the License. See accompanying LICENSE file. -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +# Enforce CMake v3.20 on MacOS else set minimum to v3.16 +if (APPLE) + cmake_minimum_required(VERSION 3.20 FATAL_ERROR) +else () + cmake_minimum_required(VERSION 3.16 FATAL_ERROR) +endif () project(helib_superbuild LANGUAGES C CXX) @@ -108,6 +113,17 @@ if (NOT TARGET_ARCHITECTURE) FORCE) endif (NOT TARGET_ARCHITECTURE) +set(HELIB_SP_NBITS "0" + CACHE + STRING "Override the bitsize for the ciphertext primes used") +if (NOT HELIB_SP_NBITS EQUAL 0) + # Now check within bounds. NB. zero is special value to not override + if (HELIB_SP_NBITS LESS 1 OR HELIB_SP_NBITS GREATER 64) + message(FATAL_ERROR "HELIB_SP_NBITS must be in range [1, 64].") + endif() + add_compile_definitions(HELIB_SP_NBITS=${HELIB_SP_NBITS}) +endif() + # Path containing FindGMP.cmake and FindNTL.cmake list(APPEND CMAKE_MODULE_PATH "${HELIB_CMAKE_EXTRA_DIR}") @@ -125,6 +141,7 @@ option(HELIB_DEBUG "Build with HELIB_DEBUG (enables extra debugging info, but needs to be initialized)" OFF) option(ENABLE_TEST "Enable tests" OFF) +option(USE_INTEL_HEXL "Use Intel HEXL library" OFF) option(PEDANTIC_BUILD "Use -Wall -Wpedantic -Wextra -Werror during build" ON) # Add properties dependent to PACKAGE_BUILD @@ -176,6 +193,31 @@ if (ENABLE_THREADS AND Threads_NOTFOUND) message(FATAL_ERROR "Cannot find pthreads (ENABLE_THREADS is ON).") endif () +# Build optional Intel HEXL library +if(USE_INTEL_HEXL) + # HEXL currently only supported using Library Build + if(PACKAGE_BUILD) + message(FATAL_ERROR "Intel HEXL currently not supported with Package " + "Build. Please use the Library Build when enabling Intel HEXL.") + endif(PACKAGE_BUILD) + + if(HEXL_DIR) + set(HEXL_DIR + "" + CACHE + STRING "Set location of HEXL cmake data for linking." + ) + set(INIT_HEXL_DIR ${HEXL_DIR}) + endif() + find_package(HEXL 1.2.1 HINTS ${HEXL_DIR} REQUIRED) + if(INIT_HEXL_DIR AND NOT INIT_HEXL_DIR STREQUAL HEXL_DIR) + message(WARNING "HEXL location provided '${INIT_HEXL_DIR}' \ + does not equal chosen '${HEXL_DIR}'.") + endif() + include_directories(${HEXL_INCLUDE_DIRS}) + add_compile_definitions(USE_INTEL_HEXL) +endif(USE_INTEL_HEXL) + # NOTE: Consider reconfiguring everything when PACKAGE_BUILD changes value. # Options from the previous value will remain otherwise. # Set up extra properties depending on the value of PACKAGE_BUILD @@ -334,6 +376,7 @@ if (PACKAGE_BUILD) -DFETCH_GMP=${FETCH_GMP} -DENABLE_TEST=${ENABLE_TEST} -DHELIB_DEBUG=${HELIB_DEBUG} + -DUSE_INTEL_HEXL=${USE_INTEL_HEXL} -DHELIB_PROJECT_ROOT_DIR=${HELIB_PROJECT_ROOT_DIR} -DHELIB_CMAKE_EXTRA_DIR=${HELIB_CMAKE_EXTRA_DIR} -DHELIB_INCLUDE_DIR=${HELIB_INCLUDE_DIR} diff --git a/INSTALL.md b/INSTALL.md index 798f20f8e..b882fb3e5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,36 +1,43 @@ # Building and installing HElib -The HElib build, install, and regression tests suite have been built and tested -on Ubuntu 18.04, Ubuntu 20.04, Fedora 32, Fedora 33, CentOS 7.8, CentOS 8.2, -macOS Mojave >=10.14.6, and macOS Catalina >=10.15.7. +The current HElib build, install, and regression tests suite have been built +and tested on Ubuntu 20.04 and macOS Catalina +>=10.15.7. Previous versions have also included Ubuntu 18.04, Fedora 33, CentOS +8.2, macOS Mojave >=10.14.6. There are two different ways to build and install HElib. The first one will automatically download and build the GMP and NTL dependencies and pack the libraries in a relocatable folder. The second way, instead, requires the dependencies to be installed by you and available in the system. +Intel [HEXL](https://github.com/intel/hexl) acceleration library for +homomorphic encryption support has been added. This should be treated as +experimental. Instructions to enable HEXL and link to it are given +[below](#enabling-and-linking-to-intel-hexl). + **Please read these instructions in full to better choose the type of build that - is better for you.** + is best for you.** ## General prerequisites -- GNU make >= 3.82 - pthreads -- git >= 1.8.3 (required to build and run the HElib test suite) +- git >= 2.27 (required to build and run the HElib test suite) **Linux environment:** -- g++ >= 7.3.1 -- cmake >= 3.10.2 +- GNU make >= 4.2 +- g++ >= 9.3.0 +- cmake >= 3.16 **macOS environment:** -- Apple clang >= 11.0.0 (available with the latest Xcode for the tested versions of macOS) +- Apple clang >= 12.0.0 (available with the latest Xcode for the tested versions of macOS) - Xcode Command Line Tools (can be installed with the command `xcode-select --install` in a teminal) -- cmake >= 3.17.3 (available from [CMake](https://cmake.org/) or [MacPorts +- cmake >= 3.20 (available from [CMake](https://cmake.org/) or [MacPorts Project](https://www.macports.org/) and [Homebrew](https://brew.sh/) as packages) +- GNU make >= 3.81 **For development:** @@ -209,6 +216,40 @@ step 3. **NOTE**: if linking against a non-system GMP, pass `GMP_PREFIX=` to the `./configure` step. +## Enabling and linking to Intel HEXL + +**NOTE:** It is currently only possible to use HEXL with HElib when using the +library build and when building HElib as a static library. i.e. +`-DPACKAGE_BUILD=OFF` and `-DBUILD_SHARED=OFF`. + +First you must download and build HEXL from source. Currently, HElib only +works with HEXL version 1.2. Using git this would be + +```bash +git clone https://github.com/intel/hexl --branch 1.2.1 +``` +Follow the instructions for HEXL installation in the README.md for all +available options. Note previous versions of HEXL requires the deprecated +`-DENABLE_EXPORT=ON` otherwise the cmake metadata for linking a cmake project +is not created. Modern versions do not have this flag and the metadata is +created by default. For a quick start most people will want, + +```bash +cd hexl +cmake -S . -B build/ [-DCMAKE_INSTALL_PREFIX=] +cmake --build build -j [] +cmake --install build +``` +If you do not provide an optional install location for HEXL the default is +`/usr/local`. + +To enable and link HEXL in HElib, you must configure cmake for HElib with +`-DUSE_INTEL_HEXL=ON`. If HEXL is not in the default system location or you +wish to use another installation tell HElib where to find it using +`-DHEXL_DIR=`. +There is no requirement to provide any HElib subprojects with the location of +HEXL. + ## HElib build options ### Generic options @@ -248,6 +289,8 @@ to the `./configure` step. - `GMP_DIR`: Prefix of the GMP library. - `NTL_DIR`: Prefix of the NTL library. +- `USE_INTEL_HEXL`: Enable the Intel HEXL library. +- `HEXL_DIR`: Prefix of the Intel HEXL library. # Using HElib in a project diff --git a/VERSION b/VERSION index 7ec1d6db4..ccbccc3dc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0 +2.2.0 diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 683d0692c..1bf6e33f8 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -48,7 +48,7 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "RelWithDebInfo" "Release" "MinSizeRel") # STRINGS avoids having the 2 newline characters at the end of the string. -# Alternatively it's possible to use file(READ ...) and then +# Alternatively it is possible to use file(READ ...) and then # string(REGEX REPLACE "\n$" "" HELIB_VERSION "${HELIB_VERSION}") file(STRINGS "../VERSION" HELIB_VERSION) @@ -61,7 +61,8 @@ set(TRGTS bgv_basic bgv_thinboot bgv_fatboot ckks_basic - IO) + IO + fft_bench) # Sources derived from their targets. set(SRCS "") diff --git a/benchmarks/bench_common.h b/benchmarks/bench_common.h new file mode 100644 index 000000000..27728e36e --- /dev/null +++ b/benchmarks/bench_common.h @@ -0,0 +1,19 @@ +/* Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HELIB_BENCH_COMMON_H +#define HELIB_BENCH_COMMON_H + +#define HE_BENCH_CAPTURE(adding_two_ciphertexts, tiny_params, fn) \ + BENCHMARK_CAPTURE(adding_two_ciphertexts, tiny_params, fn(tiny_params)) + +#endif // HELIB_BENCH_COMMON_H diff --git a/benchmarks/bgv_basic.cpp b/benchmarks/bgv_basic.cpp index fe75b0e6b..f25c07685 100644 --- a/benchmarks/bgv_basic.cpp +++ b/benchmarks/bgv_basic.cpp @@ -10,12 +10,26 @@ * limitations under the License. See accompanying LICENSE file. */ -#include -#include +/* Intel HEXL integration and updated benchmark. + * Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bgv_common.h" +#include #include -#include "bgv_common.h" +#include +#include namespace { @@ -31,11 +45,100 @@ static void adding_two_ciphertexts(benchmark::State& state, Meta& meta) helib::Ctxt ctxt2(meta.data->publicKey); meta.data->publicKey.Encrypt(ctxt1, ptxt1); - meta.data->publicKey.Encrypt(ctxt2, ptxt1); + meta.data->publicKey.Encrypt(ctxt2, ptxt2); // Benchmark adding ciphertexts - for (auto _ : state) - ctxt1 += ctxt2; - std::cout << "Additions performed = " << state.iterations() << std::endl; + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy += ctxt2; + } +} + +static void subtracting_two_ciphertexts(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt1(meta.data->context); + helib::Ptxt ptxt2(meta.data->context); + + ptxt1.random(); + ptxt2.random(); + + helib::Ctxt ctxt1(meta.data->publicKey); + helib::Ctxt ctxt2(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt1, ptxt1); + meta.data->publicKey.Encrypt(ctxt2, ptxt2); + // Benchmark subtracting ciphertexts + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy -= ctxt2; + } +} + +static void negating_a_ciphertext(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt(meta.data->context); + + ptxt.random(); + + helib::Ctxt ctxt(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt, ptxt); + // Benchmark negating a ciphertext + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt); + + state.ResumeTiming(); + copy.negate(); + } +} + +static void square_a_ciphertext(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt(meta.data->context); + + ptxt.random(); + + helib::Ctxt ctxt(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt, ptxt); + // Benchmark squaring a ciphertext + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt); + + state.ResumeTiming(); + copy.square(); + } +} + +static void multiplying_two_ciphertexts_no_relin(benchmark::State& state, + Meta& meta) +{ + helib::Ptxt ptxt1(meta.data->context); + helib::Ptxt ptxt2(meta.data->context); + + ptxt1.random(); + ptxt2.random(); + + helib::Ctxt ctxt1(meta.data->publicKey); + helib::Ctxt ctxt2(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt1, ptxt1); + meta.data->publicKey.Encrypt(ctxt2, ptxt2); + // Benchmark multiplying two ciphertexts without relinearization + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy.multLowLvl(ctxt2); + } } static void multiplying_two_ciphertexts(benchmark::State& state, Meta& meta) @@ -51,11 +154,33 @@ static void multiplying_two_ciphertexts(benchmark::State& state, Meta& meta) meta.data->publicKey.Encrypt(ctxt1, ptxt1); meta.data->publicKey.Encrypt(ctxt2, ptxt2); - // Benchmark adding ciphertexts - for (auto _ : state) - ctxt1.multiplyBy(ctxt2); - std::cout << "Multiplications performed = " << state.iterations() - << std::endl; + // Benchmark multiplying two ciphertexts + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy.multiplyBy(ctxt2); + } +} + +static void rotate_a_ciphertext_by1(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt(meta.data->context); + + ptxt.random(); + + helib::Ctxt ctxt(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt, ptxt); + // Benchmark rotating a ciphertext + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt); + + state.ResumeTiming(); + meta.data->ea.rotate(copy, 1); + } } static void encrypting_ciphertexts(benchmark::State& state, Meta& meta) @@ -68,7 +193,6 @@ static void encrypting_ciphertexts(benchmark::State& state, Meta& meta) // Benchmark encrypting ciphertexts for (auto _ : state) meta.data->publicKey.Encrypt(ctxt, ptxt); - std::cout << "Encryptions performed = " << state.iterations() << std::endl; } static void decrypting_ciphertexts(benchmark::State& state, Meta& meta) @@ -84,50 +208,73 @@ static void decrypting_ciphertexts(benchmark::State& state, Meta& meta) // Benchmark decrypting ciphertexts for (auto _ : state) meta.data->secretKey.Decrypt(decrypted_result, ctxt); - std::cout << "Decryptions performed = " << state.iterations() << std::endl; } Meta fn; -Params tiny_params(/*m=*/257, /*p=*/2, /*r=*/1, /*L=*/5800); -BENCHMARK_CAPTURE(adding_two_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(multiplying_two_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(encrypting_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(decrypting_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); - -Params small_params(/*m=*/8009, /*p=*/2, /*r=*/1, /*L=*/5800); -BENCHMARK_CAPTURE(adding_two_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(multiplying_two_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); -BENCHMARK_CAPTURE(encrypting_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(decrypting_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); - -Params big_params(/*m=*/32003, /*p=*/2, /*r=*/1, /*L=*/5800); -BENCHMARK_CAPTURE(adding_two_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(multiplying_two_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); -BENCHMARK_CAPTURE(encrypting_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); -BENCHMARK_CAPTURE(decrypting_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); +Params tiny_params(/*m=*/257, /*p=*/2, /*r=*/1, /*qbits=*/360); +HE_BENCH_CAPTURE(adding_two_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, tiny_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, tiny_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, tiny_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, tiny_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, tiny_params, fn); + +Params small_params(/*m=*/8009, /*p=*/2, /*r=*/1, /*qbits=*/380); +HE_BENCH_CAPTURE(adding_two_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, small_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, small_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, small_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, small_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, small_params, fn); + +Params big_params(/*m=*/32003, /*p=*/2, /*r=*/1, /*qbits=*/5800); +HE_BENCH_CAPTURE(adding_two_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, big_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, big_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, big_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, big_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, big_params, fn); + +Params hexl_F4_params(/*m=*/32768, /*p=*/65537, /*r=*/1, /*qbits=*/6400); +HE_BENCH_CAPTURE(adding_two_ciphertexts, hexl_F4_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, hexl_F4_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, hexl_F4_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, hexl_F4_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, hexl_F4_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, hexl_F4_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, hexl_F4_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, hexl_F4_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, hexl_F4_params, fn); + +Params hexl_F3_params(/*m=*/16, /*p=*/257, /*r=*/1, /*qbits=*/6400); +HE_BENCH_CAPTURE(adding_two_ciphertexts, hexl_F3_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, hexl_F3_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, hexl_F3_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, hexl_F3_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, hexl_F3_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, hexl_F3_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, hexl_F3_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, hexl_F3_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, hexl_F3_params, fn); + +Params hexl_F3d2_params(/*m=*/512, /*p=*/257, /*r=*/1, /*qbits=*/6400); +HE_BENCH_CAPTURE(adding_two_ciphertexts, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, hexl_F3d2_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, hexl_F3d2_params, fn); } // namespace diff --git a/benchmarks/bgv_common.h b/benchmarks/bgv_common.h index 0af2b78b7..e2590b97d 100644 --- a/benchmarks/bgv_common.h +++ b/benchmarks/bgv_common.h @@ -10,30 +10,35 @@ * limitations under the License. See accompanying LICENSE file. */ -#include +#ifndef HELIB_BGV_COMMON_H +#define HELIB_BGV_COMMON_H + +#include "bench_common.h" // HE_BENCH_CAPTURE #include +#include + struct Params { - const long m, p, r, L; + const long m, p, r, qbits; const std::vector gens; const std::vector ords; const std::vector mvec; Params(long _m, long _p, long _r, - long _L, + long _qbits, const std::vector& _gens = {}, const std::vector& _ords = {}, const std::vector& _mvec = {}) : - m(_m), p(_p), r(_r), L(_L), gens(_gens), ords(_ords), mvec(_mvec) + m(_m), p(_p), r(_r), qbits(_qbits), gens(_gens), ords(_ords), mvec(_mvec) {} Params(const Params& other) : Params(other.m, other.p, other.r, - other.L, + other.qbits, other.gens, other.ords, other.mvec) @@ -41,8 +46,9 @@ struct Params bool operator!=(Params& other) const { return !(*this == other); } bool operator==(Params& other) const { - return m == other.m && p == other.p && r == other.r && L == other.L && - gens == other.gens && ords == other.ords && mvec == other.mvec; + return m == other.m && p == other.p && r == other.r && + qbits == other.qbits && gens == other.gens && ords == other.ords && + mvec == other.mvec; } }; @@ -61,7 +67,7 @@ struct ContextAndKeys .m(params.m) .p(params.p) .r(params.r) - .bits(params.L) + .bits(params.qbits) .gens(params.gens) .ords(params.ords) .bootstrappable(!params.mvec.empty()) @@ -88,3 +94,5 @@ struct Meta return *this; } }; + +#endif // HELIB_BGV_COMMON_H diff --git a/benchmarks/ckks_basic.cpp b/benchmarks/ckks_basic.cpp index 6f618d63d..3cf18ea4f 100644 --- a/benchmarks/ckks_basic.cpp +++ b/benchmarks/ckks_basic.cpp @@ -10,14 +10,28 @@ * limitations under the License. See accompanying LICENSE file. */ -#include -#include -#include +/* Intel HEXL integration and updated benchmark. + * Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ckks_common.h" #include #include -#include "ckks_common.h" +#include + +#include +#include namespace { @@ -33,9 +47,115 @@ static void adding_two_ciphertexts(benchmark::State& state, Meta& meta) meta.data->publicKey.Encrypt(ctxt1, ptxt1); meta.data->publicKey.Encrypt(ctxt2, ptxt2); // Benchmark adding ciphertexts - for (auto _ : state) - ctxt1 += ctxt2; - std::cout << "Additions performed = " << state.iterations() << std::endl; + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy += ctxt2; + } +} + +static void subtracting_two_ciphertexts(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt1(meta.data->context); + helib::Ptxt ptxt2(meta.data->context); + ptxt1.random(); + ptxt2.random(); + + helib::Ctxt ctxt1(meta.data->publicKey); + helib::Ctxt ctxt2(meta.data->publicKey); + meta.data->publicKey.Encrypt(ctxt1, ptxt1); + meta.data->publicKey.Encrypt(ctxt2, ptxt2); + // Benchmark subtracting ciphertexts + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy -= ctxt2; + } +} + +static void negating_a_ciphertext(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt(meta.data->context); + + ptxt.random(); + + helib::Ctxt ctxt(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt, ptxt); + // Benchmark negating a ciphertext + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt); + + state.ResumeTiming(); + copy.negate(); + } +} + +static void square_a_ciphertext(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt(meta.data->context); + + ptxt.random(); + + helib::Ctxt ctxt(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt, ptxt); + // Benchmark squaring a ciphertext + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt); + + state.ResumeTiming(); + copy.square(); + } +} + +static void rotate_a_ciphertext_by1(benchmark::State& state, Meta& meta) +{ + helib::Ptxt ptxt(meta.data->context); + + ptxt.random(); + + helib::Ctxt ctxt(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt, ptxt); + // Benchmark adding ciphertexts + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt); + + state.ResumeTiming(); + meta.data->ea.rotate(copy, 1); + } +} + +static void multiplying_two_ciphertexts_no_relin(benchmark::State& state, + Meta& meta) +{ + helib::Ptxt ptxt1(meta.data->context); + helib::Ptxt ptxt2(meta.data->context); + + ptxt1.random(); + ptxt2.random(); + + helib::Ctxt ctxt1(meta.data->publicKey); + helib::Ctxt ctxt2(meta.data->publicKey); + + meta.data->publicKey.Encrypt(ctxt1, ptxt1); + meta.data->publicKey.Encrypt(ctxt2, ptxt2); + // Benchmark multiplying two ciphertexts + for (auto _ : state) { + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy.multLowLvl(ctxt2); + } } static void multiplying_two_ciphertexts(benchmark::State& state, Meta& meta) @@ -51,11 +171,13 @@ static void multiplying_two_ciphertexts(benchmark::State& state, Meta& meta) meta.data->publicKey.Encrypt(ctxt2, ptxt2); // Benchmark multiplying ciphertexts for (auto _ : state) { - ctxt1.multiplyBy(ctxt2); + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy.multiplyBy(ctxt2); benchmark::DoNotOptimize(ctxt1); } - std::cout << "Multiplications performed = " << state.iterations() - << std::endl; } static void encrypting_ciphertexts(benchmark::State& state, Meta& meta) @@ -70,7 +192,6 @@ static void encrypting_ciphertexts(benchmark::State& state, Meta& meta) meta.data->publicKey.Encrypt(ctxt, ptxt); benchmark::DoNotOptimize(ctxt); } - std::cout << "Encryptions performed = " << state.iterations() << std::endl; } static void decrypting_ciphertexts(benchmark::State& state, Meta& meta) @@ -87,7 +208,6 @@ static void decrypting_ciphertexts(benchmark::State& state, Meta& meta) meta.data->secretKey.Decrypt(decrypted_result, ctxt); benchmark::DoNotOptimize(decrypted_result); } - std::cout << "Decryptions performed = " << state.iterations() << std::endl; } static void multiply_and_add_two_ciphertexts(benchmark::State& state, @@ -95,6 +215,7 @@ static void multiply_and_add_two_ciphertexts(benchmark::State& state, { helib::Ptxt ptxt1(meta.data->context); helib::Ptxt ptxt2(meta.data->context); + ptxt1.random(); ptxt2.random(); @@ -105,67 +226,50 @@ static void multiply_and_add_two_ciphertexts(benchmark::State& state, meta.data->publicKey.Encrypt(ctxt2, ptxt2); // Benchmark multiplying and adding ciphertexts for (auto _ : state) { - ctxt1.multiplyBy(ctxt2); + state.PauseTiming(); + auto copy(ctxt1); + + state.ResumeTiming(); + copy.multiplyBy(ctxt2); benchmark::DoNotOptimize(ctxt1 += ctxt2); } - std::cout << "Operations performed = " << state.iterations() << std::endl; } Meta fn; -Params tiny_params(1024, 1, 5800); -BENCHMARK_CAPTURE(adding_two_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200) - ->Complexity(); -BENCHMARK_CAPTURE(multiplying_two_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(encrypting_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(decrypting_ciphertexts, tiny_params, fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(multiply_and_add_two_ciphertexts, - tiny_params, - fn(tiny_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); - -Params small_params(16384, 1, 5800); -BENCHMARK_CAPTURE(adding_two_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); -BENCHMARK_CAPTURE(multiplying_two_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); -BENCHMARK_CAPTURE(encrypting_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(decrypting_ciphertexts, small_params, fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(multiply_and_add_two_ciphertexts, - small_params, - fn(small_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); - -Params big_params(65536, 1, 5800); -BENCHMARK_CAPTURE(adding_two_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); -BENCHMARK_CAPTURE(multiplying_two_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->MinTime(200); -BENCHMARK_CAPTURE(encrypting_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(decrypting_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); -BENCHMARK_CAPTURE(multiply_and_add_two_ciphertexts, big_params, fn(big_params)) - ->Unit(benchmark::kMillisecond) - ->Iterations(200); +Params tiny_params(/*m=*/1024, /*precision=*/1, /*qbits=*/360); +HE_BENCH_CAPTURE(adding_two_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, tiny_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, tiny_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, tiny_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, tiny_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, tiny_params, fn); +HE_BENCH_CAPTURE(multiply_and_add_two_ciphertexts, tiny_params, fn); + +Params small_params(/*m=*/16384, /*precision=*/1, /*qbits=*/360); +HE_BENCH_CAPTURE(adding_two_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, small_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, small_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, small_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, small_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, small_params, fn); +HE_BENCH_CAPTURE(multiply_and_add_two_ciphertexts, small_params, fn); + +Params big_params(/*m=*/65536, /*precision=*/1, /*qbits=*/440); +HE_BENCH_CAPTURE(adding_two_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(subtracting_two_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(negating_a_ciphertext, big_params, fn); +HE_BENCH_CAPTURE(square_a_ciphertext, big_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts_no_relin, big_params, fn); +HE_BENCH_CAPTURE(multiplying_two_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(rotate_a_ciphertext_by1, big_params, fn); +HE_BENCH_CAPTURE(encrypting_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(decrypting_ciphertexts, big_params, fn); +HE_BENCH_CAPTURE(multiply_and_add_two_ciphertexts, big_params, fn); } // namespace diff --git a/benchmarks/ckks_common.h b/benchmarks/ckks_common.h index 49d15fded..2f4aaa00c 100644 --- a/benchmarks/ckks_common.h +++ b/benchmarks/ckks_common.h @@ -10,10 +10,15 @@ * limitations under the License. See accompanying LICENSE file. */ -#include +#ifndef HELIB_CKKS_COMMON_H +#define HELIB_CKKS_COMMON_H + +#include "bench_common.h" // HE_BENCH_CAPTURE #include +#include + struct Params { const long m, r, L; @@ -64,3 +69,5 @@ struct Meta return *this; } }; + +#endif // HELIB_CKKS_COMMON_H diff --git a/benchmarks/fft_bench.cpp b/benchmarks/fft_bench.cpp new file mode 100644 index 000000000..ff0b0e331 --- /dev/null +++ b/benchmarks/fft_bench.cpp @@ -0,0 +1,84 @@ +/* Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bgv_common.h" + +#include +#include +#include "../src/PrimeGenerator.h" // Private header + +#include +#include + +namespace { + +static void helib_fft_forward(benchmark::State& state, Meta& meta) +{ + NTL::SetNumThreads(1); + + long N = meta.data->ea.size(); + long m = meta.data->context.getM(); + auto zms = meta.data->context.getZMStar(); + + helib::PrimeGenerator prime_generator(49, m); + + long q = prime_generator.next(); + helib::Cmodulus cmod(zms, q, 0); + + NTL::ZZX poly; + poly.SetLength(N); + for (long i = 0; i < N; ++i) + poly[i] = i; + + NTL::vec_long transformed; + for (auto _ : state) + cmod.FFT(transformed, poly); +} + +static void helib_fft_inverse(benchmark::State& state, Meta& meta) +{ + NTL::SetNumThreads(1); + + long N = meta.data->ea.size(); + long m = meta.data->context.getM(); + auto zms = meta.data->context.getZMStar(); + + helib::PrimeGenerator prime_generator(49, m); + + long q = prime_generator.next(); + helib::Cmodulus cmod(zms, q, 0); + + NTL::zz_pX inverse; + inverse.SetLength(N); + + NTL::vec_long transformed(NTL::INIT_SIZE, N); + for (long i = 0; i < N; ++i) + transformed[i] = i; + + for (auto _ : state) + cmod.iFFT(inverse, transformed); +} + +Meta fn; +Params hexl_F4_params(/*m=*/16384, /*p=*/65537, /*r=*/1, /*qbits=*/5800); +HE_BENCH_CAPTURE(helib_fft_forward, hexl_F4_params, fn); //->Iterations(200); +HE_BENCH_CAPTURE(helib_fft_inverse, hexl_F4_params, fn); //->Iterations(200); + +Params hexl_F3_params(/*m=*/16, /*p=*/257, /*r=*/1, /*qbits=*/5800); +HE_BENCH_CAPTURE(helib_fft_forward, hexl_F3_params, fn); //->Iterations(200); +HE_BENCH_CAPTURE(helib_fft_inverse, hexl_F3_params, fn); //->Iterations(200); + +Params hexl_F3d2_params(/*m=*/512, /*p=*/257, /*r=*/1, /*qbits=*/5800); +HE_BENCH_CAPTURE(helib_fft_forward, hexl_F3d2_params, fn); //->Iterations(200); +HE_BENCH_CAPTURE(helib_fft_inverse, hexl_F3d2_params, fn); //->Iterations(200); + +} // namespace diff --git a/include/helib/CModulus.h b/include/helib/CModulus.h index 6f348e810..af86d602e 100644 --- a/include/helib/CModulus.h +++ b/include/helib/CModulus.h @@ -41,6 +41,7 @@ namespace helib { **/ class Cmodulus { +private: //! The modulus long q; //! PrepMulMod(q); @@ -76,6 +77,9 @@ class Cmodulus // Allocate memory and compute roots void privateInit(const PAlgebra&, long rt); + // auxiliary routine used by the two FFT routines + void FFT_aux(NTL::vec_long& y, NTL::zz_pX& tmp) const; + public: #ifdef HELIB_OPENCL SmartPtr altFFTInfo; @@ -83,12 +87,8 @@ class Cmodulus // is immutable #endif - // Destructor and constructors - //! Default constructor - Cmodulus() {} - - Cmodulus(const Cmodulus& other) { *this = other; } + Cmodulus() = default; /** * @brief Constructor @@ -97,6 +97,9 @@ class Cmodulus */ Cmodulus(const PAlgebra& zms, long qq, long rt); + //! Copy constructor + Cmodulus(const Cmodulus& other) { *this = other; }; + //! Copy assignment operator Cmodulus& operator=(const Cmodulus& other); @@ -121,9 +124,6 @@ class Cmodulus // y = FFT(x) void FFT(NTL::vec_long& y, const zzX& x) const; - // auxiliary routine used by above two routines - void FFT_aux(NTL::vec_long& y, NTL::zz_pX& tmp) const; - // expects zp context to be set externally // x = FFT^{-1}(y) void iFFT(NTL::zz_pX& x, const NTL::vec_long& y) const; diff --git a/include/helib/Ctxt.h b/include/helib/Ctxt.h index 5e92de509..da4be8176 100644 --- a/include/helib/Ctxt.h +++ b/include/helib/Ctxt.h @@ -54,11 +54,13 @@ * ciphertext wrt (1,s(X^2)) to another wrt (1,s(X^2),s^2(X^2)), but not * to another ciphertext wrt (1,s). **/ -#include // DBL_MAX + #include #include #include +#include // DBL_MAX + namespace helib { struct CKKS; struct BGV; @@ -568,23 +570,13 @@ class Ctxt // Multiply by another ciphertext void multLowLvl(const Ctxt& other, bool destructive = false); -#if 0 - [[deprecated]] - Ctxt& operator*=(const Ctxt& other) - { - multLowLvl(other); - return *this; - } -#else - // we now do the high-level mul + // This is a high-level mul with relinearization Ctxt& operator*=(const Ctxt& other) { multiplyBy(other); return *this; } -#endif - void automorph(long k); // Apply automorphism F(X) -> F(X^k) (gcd(k,m)=1) Ctxt& operator>>=(long k) { @@ -612,8 +604,8 @@ class Ctxt * Please use `Ctxt::operator*=(const EncodedPtxt& ptxt)` instead. **/ [[deprecated( - "Please use Ctxt::operator*=(const EncodedPtxt& ptxt) instead.")]] - Ctxt& operator*=(const NTL::ZZX& poly); + "Please use Ctxt::operator*=(const EncodedPtxt& ptxt) instead.")]] Ctxt& + operator*=(const NTL::ZZX& poly); //! Add a constant polynomial. //! If provided, size should be a high-probability bound @@ -656,18 +648,19 @@ class Ctxt * @deprecated This function is deprecated in favor of a newer API. * Please use `Ctxt::operator+=(double ptxt)` instead. **/ - [[deprecated("Please use Ctxt::operator+=(double ptxt) instead.")]] - void addConstantCKKS(std::pair); + [[deprecated("Please use Ctxt::operator+=(double ptxt) instead.")]] void + addConstantCKKS(std::pair); /** * @deprecated This function is deprecated in favor of a newer API. * Please use `Ctxt::operator+=(double ptxt)` instead. **/ - [[deprecated("Please use Ctxt::operator+=(double ptxt) instead.")]] - void addConstantCKKS(double x) + [[deprecated("Please use Ctxt::operator+=(double ptxt) instead.")]] void + addConstantCKKS(double x) { // FIXME: not enough precision when x is large // This function is deprecated. -// addConstantCKKS( -// rationalApprox(x, /*denomBound=*/1L << getContext().getAlMod().getR())); + // addConstantCKKS( + // rationalApprox(x, /*denomBound=*/1L << + // getContext().getAlMod().getR())); auto p = rationalApprox(x, /*denomBound=*/1L << getContext().getAlMod().getR()); *this += double(p.first) / p.second; @@ -687,17 +680,18 @@ class Ctxt * Please use `Ctxt::operator+=(const EncodedPtxt& ptxt)` instead. **/ [[deprecated( - "Please use Ctxt::operator+=(const EncodedPtxt& ptxt) instead.")]] - void addConstantCKKS(const NTL::ZZX& poly, - NTL::xdouble size = NTL::xdouble(-1.0), - NTL::xdouble factor = NTL::xdouble(-1.0)); + "Please use Ctxt::operator+=(const EncodedPtxt& ptxt) instead.")]] void + addConstantCKKS(const NTL::ZZX& poly, + NTL::xdouble size = NTL::xdouble(-1.0), + NTL::xdouble factor = NTL::xdouble(-1.0)); /** * @deprecated This function is deprecated in favor of a newer API. * Please use `Ctxt::operator+=(const PtxtArray& ptxt)` instead. **/ - [[deprecated("Please use Ctxt::operator+=(const PtxtArray& ptxt) instead.")]] - void addConstantCKKS(const std::vector>& ptxt); + [[deprecated( + "Please use Ctxt::operator+=(const PtxtArray& ptxt) instead.")]] void + addConstantCKKS(const std::vector>& ptxt); /** * @brief Add a `CKKS` plaintext to this `Ctxt`. @@ -706,14 +700,15 @@ class Ctxt * Please use `Ctxt::operator+=(const PtxtArray& ptxt)` instead. **/ [[deprecated( - "Please use Ctxt::operator+=(const Ptxt& ptxt) instead.")]] - void addConstantCKKS(const Ptxt& ptxt); + "Please use Ctxt::operator+=(const Ptxt& ptxt) instead.")]] void + addConstantCKKS(const Ptxt& ptxt); /** * @deprecated This function is deprecated in favor of a newer API. * Please use `Ctxt::operator+=(const NTL::ZZ& ptxt)` instead. **/ - [[deprecated("Please use Ctxt::operator+=(const NTL::ZZ& ptxt) instead.")]] - void addConstantCKKS(const NTL::ZZ& c); + [[deprecated( + "Please use Ctxt::operator+=(const NTL::ZZ& ptxt) instead.")]] void + addConstantCKKS(const NTL::ZZ& c); //! Multiply-by-constant. //! If the size is not given, for the DCRT variant, we use @@ -1101,8 +1096,8 @@ class Ctxt * @deprecated This function is deprecated in favor of a newer API. * Please use `Ctxt::operator*=(double ptxt)` instead. **/ - [[deprecated("Please use Ctxt::operator*=(double ptxt) instead.")]] - void multByConstantCKKS(double x) + [[deprecated("Please use Ctxt::operator*=(double ptxt) instead.")]] void + multByConstantCKKS(double x) { if (this->isEmpty()) return; @@ -1126,8 +1121,8 @@ class Ctxt * @deprecated This function is deprecated in favor of a newer API. * Please use `Ctxt::operator*=(double ptxt)` instead. **/ - [[deprecated("Please use Ctxt::operator*=(double ptxt) instead.")]] - void multByConstantCKKS(std::pair num) // rational number + [[deprecated("Please use Ctxt::operator*=(double ptxt) instead.")]] void + multByConstantCKKS(std::pair num) // rational number { // This function is deprecated. // multByConstantCKKS(double(num.first) / num.second); @@ -1149,11 +1144,11 @@ class Ctxt * Please use `Ctxt::operator*=(const EncodedPtxt& ptxt)` instead. **/ [[deprecated( - "Please use Ctxt::operator*=(const EncodedPtxt& ptxt) instead.")]] - void multByConstantCKKS(const NTL::ZZX& poly, - NTL::xdouble size = NTL::xdouble(-1.0), - NTL::xdouble factor = NTL::xdouble(-1.0), - double roundingErr = -1.0) + "Please use Ctxt::operator*=(const EncodedPtxt& ptxt) instead.")]] void + multByConstantCKKS(const NTL::ZZX& poly, + NTL::xdouble size = NTL::xdouble(-1.0), + NTL::xdouble factor = NTL::xdouble(-1.0), + double roundingErr = -1.0) { DoubleCRT dcrt(poly, context, primeSet); multByConstantCKKS(dcrt, size, factor, roundingErr); @@ -1168,15 +1163,15 @@ class Ctxt * Please use `Ctxt::operator*=(const PtxtArray& ptxt)` instead. **/ [[deprecated( - "Please use Ctxt::operator*=(const Ptxt& ptxt) instead.")]] - void multByConstantCKKS(const Ptxt& ptxt); + "Please use Ctxt::operator*=(const Ptxt& ptxt) instead.")]] void + multByConstantCKKS(const Ptxt& ptxt); /** * @deprecated This function is deprecated in favor of a newer API. * Please use `Ctxt::operator*=(const PtxtArray& ptxt)` instead. **/ [[deprecated( - "Please use Ctxt::operator*=(const PtxtArray& ptxt) instead.")]] - void multByConstantCKKS(const std::vector>& ptxt); + "Please use Ctxt::operator*=(const PtxtArray& ptxt) instead.")]] void + multByConstantCKKS(const std::vector>& ptxt); //! Convenience method: XOR and nXOR with arbitrary plaintext space: //! a xor b = a+b-2ab = a + (1-2a)*b, @@ -1389,25 +1384,14 @@ class Ctxt bool isCKKS() const { return getContext().isCKKS(); } // Return r such that p^r = ptxtSpace - long effectiveR() const - { - long p = context.getP(); - for (long r = 1, p2r = p; r < NTL_SP_NBITS; r++, p2r *= p) { - if (p2r == ptxtSpace) - return r; - if (p2r > ptxtSpace) - throw RuntimeError("ctxt.ptxtSpace is not of the form p^r"); - } - throw RuntimeError("ctxt.ptxtSpace is not of the form p^r"); - return 0; // just to keep the compiler happy - } + long effectiveR() const; /** * @brief Returns log(noiseBound) - log(q) * @deprecated This is deprecated. Please use `Ctxt::capacity()` instead. **/ - [[deprecated("Please use Ctxt::capacity() instead.")]] - double log_of_ratio() const + [[deprecated("Please use Ctxt::capacity() instead.")]] double log_of_ratio() + const { double logNoise = (getNoiseBound() <= 0.0) ? -DBL_MAX : log(getNoiseBound()); diff --git a/include/helib/DoubleCRT.h b/include/helib/DoubleCRT.h index e0a4df2a0..0e825a07a 100644 --- a/include/helib/DoubleCRT.h +++ b/include/helib/DoubleCRT.h @@ -9,6 +9,20 @@ * See the License for the specific language governing permissions and * limitations under the License. See accompanying LICENSE file. */ + +/* Intel HEXL integration. + * Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #ifndef HELIB_DOUBLECRT_H #define HELIB_DOUBLECRT_H /** @@ -32,10 +46,8 @@ class Context; */ class DoubleCRTHelper : public IndexMapInit { -private: - long val; - public: + DoubleCRTHelper() = delete; DoubleCRTHelper(const Context& context); /** @brief the init method ensures that all rows have the same size */ @@ -48,7 +60,7 @@ class DoubleCRTHelper : public IndexMapInit } private: - DoubleCRTHelper(); // disable default constructor + long val; }; /** @@ -74,6 +86,7 @@ class DoubleCRTHelper : public IndexMapInit **/ class DoubleCRT { +private: const Context& context; // the context // the data itself: if the i'th prime is in use then map[i] is the std::vector @@ -90,23 +103,8 @@ class DoubleCRT // determined by the union of the two index sets; otherwise, the index set // of *this. - class AddFun - { - public: - long apply(long a, long b, long n) { return NTL::AddMod(a, b, n); } - }; - - class SubFun - { - public: - long apply(long a, long b, long n) { return NTL::SubMod(a, b, n); } - }; - - class MulFun - { - public: - long apply(long a, long b, long n) { return NTL::MulMod(a, b, n); } - }; + // Arithmetic operations. Only the "destructive" versions are used, + // i.e., a += b is implemented but not a + b. template DoubleCRT& Op(const DoubleCRT& other, Fun fun, bool matchIndexSets = true); @@ -291,21 +289,15 @@ class DoubleCRT DoubleCRT& Negate(const DoubleCRT& other); DoubleCRT& Negate() { return Negate(*this); } - DoubleCRT& operator+=(const DoubleCRT& other) { return Op(other, AddFun()); } - - DoubleCRT& operator+=(const NTL::ZZX& poly) { return Op(poly, AddFun()); } + DoubleCRT& operator+=(const DoubleCRT& other); + DoubleCRT& operator+=(const NTL::ZZX& poly); + DoubleCRT& operator+=(const NTL::ZZ& num); + DoubleCRT& operator+=(long num); - DoubleCRT& operator+=(const NTL::ZZ& num) { return Op(num, AddFun()); } - - DoubleCRT& operator+=(long num) { return Op(NTL::to_ZZ(num), AddFun()); } - - DoubleCRT& operator-=(const DoubleCRT& other) { return Op(other, SubFun()); } - - DoubleCRT& operator-=(const NTL::ZZX& poly) { return Op(poly, SubFun()); } - - DoubleCRT& operator-=(const NTL::ZZ& num) { return Op(num, SubFun()); } - - DoubleCRT& operator-=(long num) { return Op(NTL::to_ZZ(num), SubFun()); } + DoubleCRT& operator-=(const DoubleCRT& other); + DoubleCRT& operator-=(const NTL::ZZX& poly); + DoubleCRT& operator-=(const NTL::ZZ& num); + DoubleCRT& operator-=(long num); // These are the prefix versions, ++dcrt and --dcrt. DoubleCRT& operator++() { return (*this += 1); }; @@ -317,17 +309,10 @@ class DoubleCRT void operator--(int) { *this -= 1; }; // Multiplication - DoubleCRT& operator*=(const DoubleCRT& other) - { - // return Op(other,MulFun()); - return do_mul(other); - } - - DoubleCRT& operator*=(const NTL::ZZX& poly) { return Op(poly, MulFun()); } - - DoubleCRT& operator*=(const NTL::ZZ& num) { return Op(num, MulFun()); } - - DoubleCRT& operator*=(long num) { return Op(NTL::to_ZZ(num), MulFun()); } + DoubleCRT& operator*=(const DoubleCRT& other); + DoubleCRT& operator*=(const NTL::ZZX& poly); + DoubleCRT& operator*=(const NTL::ZZ& num); + DoubleCRT& operator*=(long num); // NOTE: the matchIndexSets business in the following routines // is DEPRECATED. The requirement is that the prime set of other @@ -336,21 +321,9 @@ class DoubleCRT // not contain the prime set of other, an exception is also raised. // Procedural equivalents, supporting also the matchIndexSets flag - void Add(const DoubleCRT& other, bool matchIndexSets = true) - { - Op(other, AddFun(), matchIndexSets); - } - - void Sub(const DoubleCRT& other, bool matchIndexSets = true) - { - Op(other, SubFun(), matchIndexSets); - } - - void Mul(const DoubleCRT& other, bool matchIndexSets = true) - { - // Op(other, MulFun(), matchIndexSets); - do_mul(other, matchIndexSets); - } + void Add(const DoubleCRT& other, bool matchIndexSets = true); + void Sub(const DoubleCRT& other, bool matchIndexSets = true); + void Mul(const DoubleCRT& other, bool matchIndexSets = true); // Division by constant DoubleCRT& operator/=(const NTL::ZZ& num); diff --git a/include/helib/PAlgebra.h b/include/helib/PAlgebra.h index b067800ef..5652afcac 100644 --- a/include/helib/PAlgebra.h +++ b/include/helib/PAlgebra.h @@ -772,18 +772,13 @@ class PAlgebraModDerived : public PAlgebraModBase long r; // counts bits of precision public: - PAlgebraModDerived(const PAlgebra& palg, long _r) : zMStar(palg), r(_r) - { - assertInRange(r, - 1l, - (long)NTL_SP_NBITS, - "Invalid bit precision r"); - } + PAlgebraModDerived(const PAlgebra& palg, long _r); PAlgebraModBase* clone() const override { return new PAlgebraModDerived(*this); } + PA_tag getTag() const override { return PA_cx_tag; } const PAlgebra& getZMStar() const override { return zMStar; } @@ -796,6 +791,7 @@ class PAlgebraModDerived : public PAlgebraModBase { throw LogicError("PAlgebraModCx::getFactorsOverZZ undefined"); } + zzX getMask_zzX(UNUSED long i, UNUSED long j) const override { throw LogicError("PAlgebraModCx::getMask_zzX undefined"); diff --git a/misc/algen/algen.py b/misc/algen/algen.py index 9a92c8ff3..74b104084 100755 --- a/misc/algen/algen.py +++ b/misc/algen/algen.py @@ -49,7 +49,7 @@ def buildRanges(match): matchRange, startRange, endRange, matchSingle, endSingle = match if matchRange != None: - if endRange < startRange: + if int(endRange) < int(startRange): raise argparse.ArgumentTypeError(\ "Range going from high to low '%s'" % matchRange) else: diff --git a/misc/algen/numth.py b/misc/algen/numth.py index c445e54c8..4e942dbfb 100755 --- a/misc/algen/numth.py +++ b/misc/algen/numth.py @@ -82,7 +82,7 @@ def factorize(n): # Factor path global - run once if factorPath != None: - res = subprocess.run(["gfactor", str(n)], stdout=subprocess.PIPE, universal_newlines=True) + res = subprocess.run([factorPath, str(n)], stdout=subprocess.PIPE, universal_newlines=True) # ignore first numer - the dividend factors = map( int, res.stdout.split()[1:] ) return defaultdict(int, Counter(factors)) diff --git a/misc/format.sh b/misc/format.sh index 75c38eaff..83bbfede0 100755 --- a/misc/format.sh +++ b/misc/format.sh @@ -16,13 +16,14 @@ FORMAT_PRG="${1:-"clang-format"}" FORMAT_CMD="${FORMAT_PRG} -style=file -i" # Check we can find clang-format -if [ -z $(which $FORMAT_PRG) ]; then +if [ -z "$(which $FORMAT_PRG)" ]; then echo "$FORMAT_PRG not found." exit 1 fi # Check format program is correct major version -if [ ! $($FORMAT_PRG --version | cut -d' ' -f3 | cut -d'.' -f1) -ge 9 ]; then +# NOTE Some OSes print extra info, but the version number appears at the end. +if [ ! $($FORMAT_PRG --version | awk '{print $NF}' | cut -d'.' -f1) -ge 9 ]; then >&2 echo "Clang-format version below 9. Require 9+." exit 2 fi @@ -43,8 +44,14 @@ function echo_header { echo "Using '${FORMAT_CMD}' to perform formatting." +arg="-regextype posix-egrep" +# find command differs between Mac and Linux +if [ "$(uname)" == "Darwin" ]; then + arg="-E" +fi + previous_dir="" -for file in $(find -E . -type f -regex '.*\.(c|h|cpp|hpp)' \ +for file in $(find . $arg -type f -regex '.*\.(c|h|cpp|hpp)' \ ! -path '*/misc/*' \ ! -path '*/build/*' \ ! -path '*/dependencies/*' \ diff --git a/misc/profiling/CMakeLists.txt b/misc/profiling/CMakeLists.txt new file mode 100644 index 000000000..7e93dfe58 --- /dev/null +++ b/misc/profiling/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2021 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# This program is Licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. See accompanying LICENSE file. + +cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) + +## Use -std=c++17 as default. +set(CMAKE_CXX_STANDARD 17) +## Disable C++ extensions +set(CMAKE_CXX_EXTENSIONS OFF) +## Require full C++ standard +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(profiling LANGUAGES CXX) + +# Define standard installation directories (GNU) +include(GNUInstallDirs) + +# STRINGS avoids having the 2 newline characters at the end of the string. +# Alternatively it's possible to use file(READ ...) and then +# string(REGEX REPLACE "\n$" "" HELIB_VERSION "${HELIB_VERSION}") +file(STRINGS "../../VERSION" HELIB_VERSION) + +find_package(helib "${HELIB_VERSION}" EXACT REQUIRED) + +add_executable(circuit basic_circuit.cpp) + +target_link_libraries(circuit helib) diff --git a/misc/profiling/basic_circuit.cpp b/misc/profiling/basic_circuit.cpp new file mode 100644 index 000000000..212674d94 --- /dev/null +++ b/misc/profiling/basic_circuit.cpp @@ -0,0 +1,170 @@ +/* Copyright (C) 2021 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + * This program is Licensed under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. See accompanying LICENSE file. + */ + +// This program performs basic operations n times and is used for profiling. + +#include + +#include +#include +#include +#include + +void add(helib::Ctxt& ctxt, int iter) +{ + for (int i = 0; i < iter; ++i) { + // Create tmp to avoid noise growth. + helib::Ctxt tmp(ctxt); + tmp += ctxt; + } +} + +void mult(helib::Ctxt& ctxt, int iter) +{ + for (int i = 0; i < iter; ++i) { + // Create tmp to avoid noise growth. + helib::Ctxt tmp(ctxt); + tmp *= ctxt; + } +} + +void rotate(helib::Ctxt& ctxt, const helib::EncryptedArray& ea, int iter) +{ + for (int i = 0; i < iter; ++i) { + // Create tmp to avoid noise growth. + helib::Ctxt tmp(ctxt); + ea.rotate(tmp, 1); + } +} + +void scalarAdd(helib::Ctxt ctxt, int iter) +{ + for (int i = 0; i < iter; ++i) { + ctxt += 1l; + } +} + +void scalarMult(helib::Ctxt ctxt, int iter) +{ + for (int i = 0; i < iter; ++i) { + ctxt *= 2l; + } +} + +int main(int argc, char* argv[]) +{ + // Plaintext prime modulus + unsigned long p = 131; + // Cyclotomic polynomial - defines phi(m) + unsigned long m = 130; // this will give 48 slots + // Hensel lifting (default = 1) + unsigned long r = 1; + // Number of bits of the modulus chain + unsigned long bits = 1000; + // Number of columns of Key-Switching matrix (default = 2 or 3) + unsigned long c = 2; + // Size of NTL thread pool (default =1) + unsigned long nthreads = 1; + + helib::ArgMap() + .optional() + .named() + .arg("m", m, "Cyclotomic polynomial ring") + .arg("p", p, "Plaintext prime modulus") + .arg("r", r, "Hensel lifting") + .arg("bits", bits, "# of bits in the modulus chain") + .arg("c", c, "# fo columns of Key-Switching matrix") + .arg("nthreads", nthreads, "Size of NTL thread pool") + .parse(argc, argv); + + // set NTL Thread pool size + if (nthreads > 1) + NTL::SetNumThreads(nthreads); + + helib::Context context = helib::ContextBuilder() + .m(m) + .p(p) + .r(r) + .bits(bits) + .c(c) + .build(); + + helib::SecKey secret_key = helib::SecKey(context); + secret_key.GenSecKey(); + helib::addSome1DMatrices(secret_key); + const helib::PubKey& public_key = secret_key; + const helib::EncryptedArray& ea = context.getEA(); + + std::cout << std::endl; + context.printout(); + + long nslots = ea.size(); + + helib::PtxtArray pa(context); + pa.random(); + helib::Ctxt ctxt(public_key); + pa.encrypt(ctxt); + + int opt = 0; + std::cout << "What operation(s) do you want to run? [0:all, 1:add, 2:mult, " + "3:rotate, 4:scalarAdd, 5:scalarMult]: "; + std::cin >> opt; + + int iter = 0; + std::cout << "How many iterations?: "; + std::cin >> iter; + iter = (iter < 1) ? 10 : iter; + + std::cout << "Option: " << opt << ", " << iter << " times.\n"; + + switch (opt) { + case 0: + std::cout << "Running option " << opt << ": all operations...\n"; + add(ctxt, iter); + mult(ctxt, iter); + rotate(ctxt, ea, iter); + scalarAdd(ctxt, iter); + scalarMult(ctxt, iter); + std::cout << "Done!\n"; + break; + case 1: + std::cout << "Running option " << opt << ": additions...\n"; + add(ctxt, iter); + std::cout << "Done!\n"; + break; + case 2: + std::cout << "Running option " << opt << ": multiplications...\n"; + mult(ctxt, iter); + std::cout << "Done!\n"; + break; + case 3: + std::cout << "Running option " << opt << ": rotations...\n"; + rotate(ctxt, ea, iter); + std::cout << "Done!\n"; + break; + case 4: + std::cout << "Running option " << opt << ": scalar additions...\n"; + scalarAdd(ctxt, iter); + std::cout << "Done!\n"; + break; + case 5: + std::cout << "Running option " << opt << ": scalar multiplications...\n"; + scalarMult(ctxt, iter); + std::cout << "Done!\n"; + break; + default: + std::cout << "Received invalid option: " << opt << std::endl; + } + + return 0; +} diff --git a/misc/psi/README.md b/misc/psi/README.md index b7b769dc4..2eb2d1ccd 100644 --- a/misc/psi/README.md +++ b/misc/psi/README.md @@ -31,22 +31,22 @@ columns, where the data can be seen in bold
 2 4
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[0,1],[2,3],[4,5]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[0],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[6,7],[8,9],[10,11]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[1],[1],[1]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[12,13],[14,15],[16,17]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[2],[2],[2]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[18,19],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[3],[3],[3]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0,1],[2,3],[4,5]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[6,7],[8,9],[10,11]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[1],[1],[1]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[12,13],[14,15],[16,17]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[2],[2],[2]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[18,19],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[3],[3],[3]]},"serializationVersion":"0.0.1","type":"Ptxt"}
 
and an example query
 1 4
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[0,1],[2,3],[4,5]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[6,7],[8,9],[10,11]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[12,13],[14,15],[16,17]]},"serializationVersion":"0.0.1","type":"Ptxt"}
-{"HElibVersion":"2.1.0","content":{"scheme":"BGV","slots":[[18,19],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0,1],[2,3],[4,5]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[6,7],[8,9],[10,11]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[12,13],[14,15],[16,17]]},"serializationVersion":"0.0.1","type":"Ptxt"}
+{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[18,19],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
 
Make sure you are in the directory `/HElib/misc/psi/test` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f247ecc51..ca665949a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,6 +105,7 @@ set(HELIB_SRCS "fhe_stats.cpp" "hypercube.cpp" "IndexSet.cpp" + "intelExt.cpp" "intraSlot.cpp" "JsonWrapper.cpp" "keys.cpp" @@ -297,6 +298,28 @@ target_link_libraries( # Add pthread if required $<$:Threads::Threads>) +# Link HEXL +if (USE_INTEL_HEXL) + add_dependencies(helib HEXL::hexl) + + # Manually add the HEXL include directory + get_target_property( + HEXL_INCLUDE_DIR + HEXL::hexl + INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(helib PRIVATE ${HEXL_INCLUDE_DIR}) + + # Manually package HEXL with libhelib.a + add_custom_command(TARGET helib POST_BUILD + COMMAND ar -x $ + COMMAND ar -x $ + COMMAND ar -qcs $ *.o + COMMAND rm -f *.o + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS helib hexl + ) +endif() + # FIX RPATH if (BUILD_SHARED) # RPATH will be empty since ntl, gmp, and helib are all in PACKAGE_DIR/lib diff --git a/src/CModulus.cpp b/src/CModulus.cpp index 7857a5770..9e8af5e96 100644 --- a/src/CModulus.cpp +++ b/src/CModulus.cpp @@ -9,6 +9,20 @@ * See the License for the specific language governing permissions and * limitations under the License. See accompanying LICENSE file. */ + +/* Intel HEXL integration. + * Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /* CModulus.cpp - supports forward and backward length-m FFT transformations * * This is a wrapper around the bluesteinFFT routines, for one modulus q. @@ -27,6 +41,10 @@ #include #include +#ifdef USE_INTEL_HEXL +#include "intelExt.h" +#endif + namespace helib { // It is assumed that m,q,context, and root are already set. If root is set @@ -43,25 +61,22 @@ NTL::zz_pContext BuildContext(long p, long maxroot) // Constructor: it is assumed that zms is already set with m>1 // If q == 0, then the current context is used -Cmodulus::Cmodulus(const PAlgebra& zms, long qq, long rt) +Cmodulus::Cmodulus(const PAlgebra& zms, long qq, long rt) : + q(qq), zMStar(&zms), root(rt) { assertTrue(zms.getM() > 1, "Bad Z_m^* modulus m (must be greater than 1)"); bool explicitModulus = true; - if (qq == 0) { - q = NTL::zz_p::modulus(); + // Correction for q == 0 + if (this->q == 0) { + this->q = NTL::zz_p::modulus(); explicitModulus = false; - } else - q = qq; + } qinv = NTL::PrepMulMod(q); - zMStar = &zms; - root = rt; - - long mm; - mm = zms.getM(); + long mm = zms.getM(); m_inv = NTL::InvMod(mm, q); NTL::zz_pBak bak; @@ -223,10 +238,8 @@ Cmodulus& Cmodulus::operator=(const Cmodulus& other) static long RevInc(long a, long k) { - long j, m; - - j = k; - m = 1L << (k - 1); + long j = k; + long m = 1L << (k - 1); while (j && (m & a)) { a ^= m; @@ -263,8 +276,7 @@ static long* BRC_init(long k) long n = (1L << k); brc_mem[k].SetLength(n); long* rev = brc_mem[k].elts(); - long i, j; - for (i = 0, j = 0; i < n; i++, j = RevInc(j, k)) + for (long i = 0, j = 0; i < n; i++, j = RevInc(j, k)) rev[i] = j; return rev; } @@ -295,9 +307,10 @@ static void COBRA(long* NTL_RESTRICT B, const long* NTL_RESTRICT A, long k) long q = NTL_BRC_Q; long k1 = k - 2 * q; - long *NTL_RESTRICT rev_k1, *NTL_RESTRICT rev_q; + long* NTL_RESTRICT rev_k1; + long* NTL_RESTRICT rev_q; long* NTL_RESTRICT T; - long a, b, c, a1, b1, c1; + long a1, b1, c1; rev_k1 = brc_mem[k1].elts(); if (!rev_k1) @@ -313,17 +326,17 @@ static void COBRA(long* NTL_RESTRICT B, const long* NTL_RESTRICT A, long k) T = BRC_temp.elts(); } - for (b = 0; b < (1L << k1); b++) { + for (long b = 0; b < (1L << k1); b++) { b1 = rev_k1[b]; - for (a = 0; a < (1L << q); a++) { + for (long a = 0; a < (1L << q); a++) { a1 = rev_q[a]; - for (c = 0; c < (1L << q); c++) + for (long c = 0; c < (1L << q); c++) T[(a1 << q) + c] = A[(a << (k1 + q)) + (b << q) + c]; } - for (c = 0; c < (1L << q); c++) { + for (long c = 0; c < (1L << q); c++) { c1 = rev_q[c]; - for (a1 = 0; a1 < (1L << q); a1++) + for (long a1 = 0; a1 < (1L << q); a1++) B[(c1 << (k1 + q)) + (b1 << q) + a1] = T[(a1 << q) + c]; } } @@ -347,54 +360,70 @@ void Cmodulus::FFT_aux(NTL::vec_long& y, NTL::zz_pX& tmp) const HELIB_TIMER_START; if (zMStar->getPow2()) { - // special case when m is a power of 2 + // Special case: m is a power of 2 long k = zMStar->getPow2(); long phim = (1L << (k - 1)); long dx = deg(tmp); long p = NTL::zz_p::modulus(); - const NTL::zz_p* powers_p = (*powers).rep.elts(); - const NTL::mulmod_precon_t* powers_aux_p = powers_aux.elts(); - y.SetLength(phim); long* yp = y.elts(); NTL::zz_p* tmp_p = tmp.rep.elts(); - for (long i = 0; i <= dx; i++) +#ifdef USE_INTEL_HEXL + + for (long i = 0; i <= dx; ++i) { + yp[i] = rep(tmp_p[i]); + } + + for (long i = dx + 1; i < phim; ++i) { + yp[i] = 0; + } + + intel::FFTFwd(yp, yp, phim, p); + +#else + + const NTL::zz_p* powers_p = (*powers).rep.elts(); + const NTL::mulmod_precon_t* powers_aux_p = powers_aux.elts(); + + for (long i = 0; i <= dx; i++) { yp[i] = NTL::MulModPrecon(rep(tmp_p[i]), rep(powers_p[i]), p, powers_aux_p[i]); - for (long i = dx + 1; i < phim; i++) + } + + for (long i = dx + 1; i < phim; i++) { yp[i] = 0; + } #ifdef HELIB_OPENCL AltFFTFwd(yp, yp, k - 1, *altFFTInfo); #else #ifndef NTL_PROVIDES_TRUNC_FFT - FFTFwd(yp, yp, k - 1, *NTL::zz_pInfo->p_info); + NTL::FFTFwd(yp, yp, k - 1, *NTL::zz_pInfo->p_info); #else + NTL::FFTFwd(yp, yp, k - 1, *NTL::zz_pInfo->p_info); +#endif + +#endif // HELIB_OPENCL + +#endif // USE_INTEL_HEXL - FFTFwd(yp, yp, k - 1, *NTL::zz_pInfo->p_info); // Now we have to bit reverse the result // The BitReverseCopy routine does not allow aliasing, so // we have to do an extra copy here. // We use the fact tmp1 and y do not alias. + NTL::vec_long& bit_reversed = Cmodulus::getScratch_vec_long(); + bit_reversed.SetLength(phim); + long* bit_reversed_p = bit_reversed.elts(); - NTL::vec_long& tmp1 = Cmodulus::getScratch_vec_long(); - tmp1.SetLength(phim); - long* tmp1_p = tmp1.elts(); - - BitReverseCopy(tmp1_p, yp, k - 1); - for (long i = 0; i < phim; i++) - yp[i] = tmp1_p[i]; - -#endif - -#endif + BitReverseCopy(bit_reversed_p, yp, k - 1); + std::copy_n(bit_reversed_p, phim, yp); return; } @@ -408,9 +437,8 @@ void Cmodulus::FFT_aux(NTL::vec_long& y, NTL::zz_pX& tmp) const // copy the result to the output vector y, keeping only the // entries corresponding to primitive roots of unity y.SetLength(zMStar->getPhiM()); - long i, j; - long m = getM(); - for (i = j = 0; i < m; i++) + + for (long i = 0, j = 0; i < long(this->getM()); i++) if (zMStar->inZmStar(i)) y[j++] = rep(coeff(tmp, i)); } @@ -461,51 +489,67 @@ void Cmodulus::iFFT(NTL::zz_pX& x, const NTL::vec_long& y) const long phim = (1L << (k - 1)); long p = NTL::zz_p::modulus(); - const NTL::zz_p* ipowers_p = (*ipowers).rep.elts(); - const NTL::mulmod_precon_t* ipowers_aux_p = ipowers_aux.elts(); - const long* yp = y.elts(); NTL::vec_long& tmp = Cmodulus::getScratch_vec_long(); tmp.SetLength(phim); long* tmp_p = tmp.elts(); + // We have to bit reverse the inputs to FFTRev1 + // The BitReverseCopy routine does not allow aliasing. + // We use the fact that y and tmp do not alias + + BitReverseCopy(tmp_p, yp, k - 1); + +#ifdef USE_INTEL_HEXL + + intel::FFTRev1(tmp_p, tmp_p, phim, p); + + x.rep.SetLength(phim); + NTL::zz_p* xp = x.rep.elts(); + + for (long i = 0; i < phim; ++i) { + xp[i].LoopHole() = tmp_p[i]; + } + +#else + + const NTL::zz_p* ipowers_p = (*ipowers).rep.elts(); + const NTL::mulmod_precon_t* ipowers_aux_p = ipowers_aux.elts(); + #ifdef HELIB_OPENCL AltFFTRev1(tmp_p, yp, k - 1, *altFFTInfo); #else #ifndef NTL_PROVIDES_TRUNC_FFT - FFTRev1(tmp_p, yp, k - 1, *NTL::zz_pInfo->p_info); + NTL::FFTRev1(tmp_p, yp, k - 1, *NTL::zz_pInfo->p_info); #else - // We have to bit reverse the inputs to FFTRev1 - // The BitReverseCopy routine does not allow aliasing. - // We use the fact that y and tmp do not alias - - BitReverseCopy(tmp_p, yp, k - 1); - FFTRev1(tmp_p, tmp_p, k - 1, *NTL::zz_pInfo->p_info); + NTL::FFTRev1(tmp_p, tmp_p, k - 1, *NTL::zz_pInfo->p_info); #endif -#endif +#endif // HELIB_OPENCL x.rep.SetLength(phim); NTL::zz_p* xp = x.rep.elts(); - for (long i = 0; i < phim; i++) + for (long i = 0; i < phim; ++i) { xp[i].LoopHole() = NTL::MulModPrecon(tmp_p[i], rep(ipowers_p[i]), p, ipowers_aux_p[i]); + } + +#endif // USE_INTEL_HEXL x.normalize(); return; - } + } // End of special case if m is power of 2 NTL::zz_p rt; long m = getM(); // convert input to zpx format, initializing only the coeffs i s.t. (i,m)=1 x.rep.SetLength(m); - long i, j; - for (i = j = 0; i < m; i++) + for (long i = 0, j = 0; i < m; i++) if (zMStar->inZmStar(i)) x.rep[i].LoopHole() = y[j++]; // DIRT: y[j] already reduced x.normalize(); diff --git a/src/Context.cpp b/src/Context.cpp index 4ff846191..f698110d0 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -24,6 +24,8 @@ using json = ::nlohmann::json; #include #include +#include "macro.h" +#include "PrimeGenerator.h" #include "binio.h" #include "io.h" @@ -69,99 +71,6 @@ double lweEstimateSecurity(int n, double log2AlphaInv, int hwt) return ret < 0.0 ? 0.0 : ret; // If ret is negative then return 0.0 } -// You initialize a PrimeGenerator as follows: -// PrimeGenerator gen(len, m); -// Each call to gen.next() generates a prime p with -// (1-1/2^B)*2^len <= p < 2^len and p = 2^k*t*m + 1, -// where t is odd and k is as large as possible -// and B is a small constant (typically, B in {2,3,4}). -// If no such prime is found, then an error is raised. - -struct PrimeGenerator -{ - const static long B = 3; - long len, m; - long k, t; - - PrimeGenerator(long _len, long _m) : len(_len), m(_m) - { - assertInRange(len, - long(B), - static_cast(NTL_SP_NBITS), - "PrimeGenerator: len is not " - "in [B, NTL_SP_NBITS]", - true); - assertInRange(m, - 1l, - static_cast(NTL_SP_BOUND), - "PrimeGenerator: m is " - "not in [1, NTL_SP_BOUND)"); - - // compute k as smallest non-negative integer such that - // 2^{len-B} < 2^k*m - k = 0; - while ((m << k) <= (1L << (len - B))) - k++; - - t = divc((1L << len) - 1, m << k); - // this ensures the fist call to next will trigger a new k-value - } - - long next() - { - // we consider all odd t in the interval - // [ (1-1/2^B)*2^len-1)/(2^k*m), (2^len-1)/(2^k*m) ). - // For k satisfying 2^{len-B} >= 2^k*m, this interval is - // contains at least one integer. - // It is equivalent to consider the interval - // of integers [tlb, tub), where tlb = ceil(((1-1/2^B)*2^len-1)/(2^k*m)) - // and tub = ceil((2^len-1)/(2^k*m)). - - long tub = divc((1L << len) - 1, m << k); - - for (;;) { - - t++; - - if (t >= tub) { - // move to smaller value of k, reset t and tub - - k--; - - long klb; - if (m % 2 == 0) - klb = 0; - else - klb = 1; - - // we run k down to 0 if m is even, and down to 1 - // if m is odd. - - if (k < klb) - throw RuntimeError("Prime generator ran out of primes"); - - t = divc((1L << len) - (1L << (len - B)) - 1, m << k); - tub = divc((1L << len) - 1, m << k); - } - - if (t % 2 == 0) - continue; // we only want to consider odd t - - long cand = ((t * m) << k) + 1; // = 2^k*t*m + 1 - - // double check that cand is in the prescribed interval - assertInRange(cand, - (1L << len) - (1L << (len - B)), - 1L << len, - "Candidate cand is not in the prescribed interval"); - - if (NTL::ProbPrime(cand, 60)) - return cand; - // iteration count == 60 implies 2^{-120} error probability - } - } -}; - // Useful params objects (POD) to simplify calls between ContextBuilder and // Context. struct Context::ModChainParams @@ -819,12 +728,12 @@ static void CheckPrimes(const Context& context, void Context::addSmallPrimes(long resolution, long cpSize) { // cpSize is the size of the ciphertext primes - // Sanity-checks, cpSize \in [0.9*NTL_SP_NBITS, NTL_SP_NBITS] + // Sanity-checks, cpSize \in [0.9*HELIB_SP_NBITS, HELIB_SP_NBITS] assertTrue(cpSize >= 30, "cpSize is too small (minimum is 30)"); assertInRange(cpSize * 10, - 9l * NTL_SP_NBITS, - 10l * NTL_SP_NBITS, - "cpSize not in [0.9*NTL_SP_NBITS, NTL_SP_NBITS]", + 9l * HELIB_SP_NBITS, + 10l * HELIB_SP_NBITS, + "cpSize not in [0.9*HELIB_SP_NBITS, HELIB_SP_NBITS]", true); long m = getM(); @@ -897,10 +806,10 @@ void Context::addSpecialPrime(long q) } // Determine the target size of the ctxtPrimes. The target size is -// set at 2^n, where n is at most NTL_SP_NBITS and at least -// ceil(0.9*NTL_SP_NBITS), so that we don't overshoot nBits by too +// set at 2^n, where n is at most HELIB_SP_NBITS and at least +// ceil(0.9*HELIB_SP_NBITS), so that we don't overshoot nBits by too // much. -// The reason that we do not allow to go below 0.9*NTL_SP_NBITS is +// The reason that we do not allow to go below 0.9*HELIB_SP_NBITS is // that we need some of the smallPrimes to be sufficiently smaller // than the ctxtPrimes, and still we need these smallPrimes to have // m'th roots of unity. @@ -910,20 +819,21 @@ static long ctxtPrimeSize(long nBits) -std::log1p(-1.0 / double(1L << PrimeGenerator::B)) / std::log(2.0); // std::cerr << "*** bit_loss=" << bit_loss; - // How many primes of size NTL_SP_NBITS it takes to get to nBits - double maxPsize = NTL_SP_NBITS - bit_loss; + // How many primes of size HELIB_SP_NBITS it takes to get to nBits + double maxPsize = HELIB_SP_NBITS - bit_loss; // primes of length len are guaranteed to be at least (1-1/2^B)*2^len, long nPrimes = long(ceil(nBits / maxPsize)); // this is sufficiently many primes - // now we want to trim the size to avoid unnecssary overshooting + // now we want to trim the size to avoid unnecessary overshooting // so we decrease targetSize, while guaranteeing that // nPrimes primes of length targetSize multiply out to // at least nBits bits. - long targetSize = NTL_SP_NBITS; - while (10 * (targetSize - 1) >= 9 * NTL_SP_NBITS && (targetSize - 1) >= 30 && + long targetSize = HELIB_SP_NBITS; + while (10 * (targetSize - 1) >= 9 * HELIB_SP_NBITS && + (targetSize - 1) >= 30 && ((targetSize - 1) - bit_loss) * nPrimes >= nBits) targetSize--; @@ -938,13 +848,13 @@ void Context::addCtxtPrimes(long nBits, long targetSize) // We add enough primes of size targetSize until their product is // at least 2^{nBits} - // Sanity-checks, targetSize \in [0.9*NTL_SP_NBITS, NTL_SP_NBITS] + // Sanity-checks, targetSize \in [0.9*HELIB_SP_NBITS, HELIB_SP_NBITS] assertTrue(targetSize >= 30, "Target prime is too small (minimum size is 30)"); assertInRange(targetSize * 10, - 9l * NTL_SP_NBITS, - 10l * NTL_SP_NBITS, - "targetSize not in [0.9*NTL_SP_NBITS, NTL_SP_NBITS]", + 9l * HELIB_SP_NBITS, + 10l * HELIB_SP_NBITS, + "targetSize not in [0.9*HELIB_SP_NBITS, HELIB_SP_NBITS]", true); const PAlgebra& palg = getZMStar(); long m = palg.getM(); @@ -1081,20 +991,20 @@ void Context::addSpecialPrimes(long nDgts, double bit_loss = -std::log1p(-1.0 / double(1L << PrimeGenerator::B)) / std::log(2.0); - // How many primes of size NTL_SP_NBITS it takes to get to nBits - double maxPsize = NTL_SP_NBITS - bit_loss; + // How many primes of size HELIB_SP_NBITS it takes to get to nBits + double maxPsize = HELIB_SP_NBITS - bit_loss; // primes of length len are guaranteed to be at least (1-1/2^B)*2^len, long nPrimes = long(ceil(nBits / maxPsize)); // this is sufficiently many prime - // now we want to trim the size to avoid unnecssary overshooting + // now we want to trim the size to avoid unnecessary overshooting // so we decrease targetSize, while guaranteeing that // nPrimes primes of length targetSize multiply out to // at least nBits bits. - long targetSize = NTL_SP_NBITS; - while ((targetSize - 1) >= 0.55 * NTL_SP_NBITS && (targetSize - 1) >= 30 && + long targetSize = HELIB_SP_NBITS; + while ((targetSize - 1) >= 0.55 * HELIB_SP_NBITS && (targetSize - 1) >= 30 && ((targetSize - 1) - bit_loss) * nPrimes >= nBits) targetSize--; diff --git a/src/Ctxt.cpp b/src/Ctxt.cpp index 509076330..b346fcd52 100644 --- a/src/Ctxt.cpp +++ b/src/Ctxt.cpp @@ -14,6 +14,7 @@ #include "io.h" #include "binio.h" +#include "macro.h" #include #include @@ -99,6 +100,19 @@ void SKHandle::readJSON(const JsonWrapper& jw) std::set* FHEglobals::automorphVals = nullptr; std::set* FHEglobals::automorphVals2 = nullptr; +long Ctxt::effectiveR() const +{ + long p = context.getP(); + for (long r = 1, p2r = p; r < HELIB_SP_NBITS; r++, p2r *= p) { + if (p2r == ptxtSpace) + return r; + if (p2r > ptxtSpace) + throw RuntimeError("ctxt.ptxtSpace is not of the form p^r"); + } + throw RuntimeError("ctxt.ptxtSpace is not of the form p^r"); + return 0; // just to keep the compiler happy +} + bool Ctxt::isCorrect() const { NTL::xdouble xQ = NTL::xexp(getContext().logOfProduct(getPrimeSet())); @@ -821,7 +835,7 @@ void Ctxt::keySwitchPart(const CtxtPart& p, const KeySwitch& W) HELIB_STATS_UPDATE("KS-noise-ratio", ratio); if (ratio > 1) { - Warning("KS-noise-ratio=" + std::to_string(ratio) + "\n"); + Warning("KS-noise-ratio=" + std::to_string(ratio)); } noiseBound += addedNoise; // update the noise estimate diff --git a/src/DoubleCRT.cpp b/src/DoubleCRT.cpp index f5dff38b4..e356d6d73 100644 --- a/src/DoubleCRT.cpp +++ b/src/DoubleCRT.cpp @@ -9,6 +9,20 @@ * See the License for the specific language governing permissions and * limitations under the License. See accompanying LICENSE file. */ + +/* Intel HEXL integration. + * Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /* DoubleCRT.cpp - This class holds an integer polynomial in double-CRT form * * Double-CRT form is a matrix of L rows and phi(m) columns. The i'th row @@ -23,6 +37,7 @@ #include "binio.h" #include "io.h" +#include "intelExt.h" #include #include @@ -117,8 +132,85 @@ void DoubleCRT::verify() } } -// Arithmetic operations. Only the "destructive" versions are used, -// i.e., a += b is implemented but not a + b. +#ifdef USE_INTEL_HEXL +struct AddFun +{ + void apply(long* result, + const long* a, + const long* b, + long size, + long modulus) const + { + intel::EltwiseAddMod(result, a, b, size, modulus); + } + + void apply(long* result, + const long* a, + long scalar, + long size, + long modulus) const + { + intel::EltwiseAddMod(result, a, scalar, size, modulus); + } +}; + +struct SubFun +{ + void apply(long* result, + const long* a, + const long* b, + long size, + long modulus) const + { + intel::EltwiseSubMod(result, a, b, size, modulus); + } + + void apply(long* result, + const long* a, + long scalar, + long size, + long modulus) const + { + intel::EltwiseSubMod(result, a, scalar, size, modulus); + } +}; + +struct MulFun +{ + void apply(long* result, + const long* a, + const long* b, + long size, + long modulus) const + { + intel::EltwiseMultMod(result, a, b, size, modulus); + } + + void apply(long* result, + const long* a, + long scalar, + long size, + long modulus) const + { + intel::EltwiseMultMod(result, a, scalar, size, modulus); + } +}; +#else +struct AddFun +{ + long apply(long a, long b, long n) const { return NTL::AddMod(a, b, n); } +}; + +struct SubFun +{ + long apply(long a, long b, long n) const { return NTL::SubMod(a, b, n); } +}; + +struct MulFun +{ + long apply(long a, long b, long n) const { return NTL::MulMod(a, b, n); } +}; +#endif // Generic operation, Fnc is AddMod, SubMod, or MulMod (from NTL's ZZ module) template @@ -170,8 +262,12 @@ DoubleCRT& DoubleCRT::Op(const DoubleCRT& other, Fun fun, bool matchIndexSets) NTL::vec_long& row = map[i]; const NTL::vec_long& other_row = (*other_map)[i]; +#ifdef USE_INTEL_HEXL + fun.apply(row.elts(), row.elts(), other_row.elts(), phim, pi); +#else for (long j : range(phim)) row[j] = fun.apply(row[j], other_row[j], pi); +#endif } return *this; } @@ -226,30 +322,20 @@ DoubleCRT& DoubleCRT::do_mul(const DoubleCRT& other, bool matchIndexSets) // add/sub/mul the data, element by element, modulo the respective primes for (long i : s) { long pi = context.ithPrime(i); - NTL::mulmod_t pi_inv = context.ithModulus(i).getQInv(); NTL::vec_long& row = map[i]; const NTL::vec_long& other_row = (*other_map)[i]; +#ifdef USE_INTEL_HEXL + intel::EltwiseMultMod(row.elts(), row.elts(), other_row.elts(), phim, pi); +#else + NTL::mulmod_t pi_inv = context.ithModulus(i).getQInv(); for (long j : range(phim)) row[j] = MulMod(row[j], other_row[j], pi, pi_inv); +#endif // USE_INTEL_HEXL } return *this; } -#if 0 -template -DoubleCRT& DoubleCRT::Op(const DoubleCRT &other, MulFun fun, - bool matchIndexSets); -#endif - -template DoubleCRT& DoubleCRT::Op(const DoubleCRT& other, - AddFun fun, - bool matchIndexSets); - -template DoubleCRT& DoubleCRT::Op(const DoubleCRT& other, - SubFun fun, - bool matchIndexSets); - template DoubleCRT& DoubleCRT::Op(const NTL::ZZ& num, Fun fun) { @@ -263,21 +349,17 @@ DoubleCRT& DoubleCRT::Op(const NTL::ZZ& num, Fun fun) long pi = context.ithPrime(i); long n = rem(num, pi); // n = num % pi NTL::vec_long& row = map[i]; + +#ifdef USE_INTEL_HEXL + fun.apply(row.elts(), row.elts(), n, phim, pi); +#else for (long j : range(phim)) row[j] = fun.apply(row[j], n, pi); +#endif } return *this; } -template DoubleCRT& DoubleCRT::Op(const NTL::ZZ& num, - MulFun fun); - -template DoubleCRT& DoubleCRT::Op(const NTL::ZZ& num, - AddFun fun); - -template DoubleCRT& DoubleCRT::Op(const NTL::ZZ& num, - SubFun fun); - DoubleCRT& DoubleCRT::Negate(const DoubleCRT& other) { if (isDryRun()) @@ -313,14 +395,84 @@ DoubleCRT& DoubleCRT::Op(const NTL::ZZX& poly, Fun fun) return Op(other, fun); } -template DoubleCRT& DoubleCRT::Op(const NTL::ZZX& poly, - MulFun fun); +// overloaded ops +DoubleCRT& DoubleCRT::operator+=(const DoubleCRT& other) +{ + return Op(other, AddFun()); +} + +DoubleCRT& DoubleCRT::operator+=(const NTL::ZZX& poly) +{ + return Op(poly, AddFun()); +} + +DoubleCRT& DoubleCRT::operator+=(const NTL::ZZ& num) +{ + return Op(num, AddFun()); +} + +DoubleCRT& DoubleCRT::operator+=(long num) +{ + return Op(NTL::to_ZZ(num), AddFun()); +} + +DoubleCRT& DoubleCRT::operator-=(const DoubleCRT& other) +{ + return Op(other, SubFun()); +} + +DoubleCRT& DoubleCRT::operator-=(const NTL::ZZX& poly) +{ + return Op(poly, SubFun()); +} + +DoubleCRT& DoubleCRT::operator-=(const NTL::ZZ& num) +{ + return Op(num, SubFun()); +} + +DoubleCRT& DoubleCRT::operator-=(long num) +{ + return Op(NTL::to_ZZ(num), SubFun()); +} + +DoubleCRT& DoubleCRT::operator*=(const DoubleCRT& other) +{ + // return Op(other,MulFun()); + return do_mul(other); +} + +DoubleCRT& DoubleCRT::operator*=(const NTL::ZZX& poly) +{ + return Op(poly, MulFun()); +} -template DoubleCRT& DoubleCRT::Op(const NTL::ZZX& poly, - AddFun fun); +DoubleCRT& DoubleCRT::operator*=(const NTL::ZZ& num) +{ + return Op(num, MulFun()); +} -template DoubleCRT& DoubleCRT::Op(const NTL::ZZX& poly, - SubFun fun); +DoubleCRT& DoubleCRT::operator*=(long num) +{ + return Op(NTL::to_ZZ(num), MulFun()); +} + +// Function versions +void DoubleCRT::Add(const DoubleCRT& other, bool matchIndexSets) +{ + Op(other, AddFun(), matchIndexSets); +} + +void DoubleCRT::Sub(const DoubleCRT& other, bool matchIndexSets) +{ + Op(other, SubFun(), matchIndexSets); +} + +void DoubleCRT::Mul(const DoubleCRT& other, bool matchIndexSets) +{ + // Op(other, MulFun(), matchIndexSets); + do_mul(other, matchIndexSets); +} // break *this into n digits,according to the primeSets in context.digits // returns the sum of the canonical embedding norms of the digits diff --git a/src/NumbTh.cpp b/src/NumbTh.cpp index 4d6595493..5e60bcd95 100644 --- a/src/NumbTh.cpp +++ b/src/NumbTh.cpp @@ -9,6 +9,9 @@ * See the License for the specific language governing permissions and * limitations under the License. See accompanying LICENSE file. */ + +#include "macro.h" // Private Header + #include #include #include @@ -1603,7 +1606,7 @@ std::pair rationalApprox(double x, long denomBound) x = -x; } if (denomBound <= 0) - denomBound = 1L << (NTL_SP_NBITS / 2); + denomBound = 1L << (HELIB_SP_NBITS / 2); double epsilon = 1.0 / (denomBound * 8.0); // "smudge factor" double a = floor(x + epsilon); double xi = x - a; @@ -1642,7 +1645,7 @@ std::pair rationalApprox(NTL::xdouble x, x = -x; } if (denomBound <= 0) - denomBound = NTL::conv(1L << (NTL_SP_NBITS / 2)); + denomBound = NTL::conv(1L << (HELIB_SP_NBITS / 2)); NTL::xdouble epsilon = 0.125 / denomBound; // "smudge factor" NTL::xdouble a = floor(x + epsilon); diff --git a/src/PAlgebra.cpp b/src/PAlgebra.cpp index d301b45d9..b24f9de1d 100644 --- a/src/PAlgebra.cpp +++ b/src/PAlgebra.cpp @@ -10,6 +10,8 @@ * limitations under the License. See accompanying LICENSE file. */ +#include "macro.h" + #include #include #include @@ -428,6 +430,16 @@ double calcPolyNormBnd(long m) return max_norm; } +PAlgebraModDerived::PAlgebraModDerived(const PAlgebra& palg, long _r) : + zMStar(palg), r(_r) +{ + assertInRange(r, + 1l, + (long)HELIB_SP_NBITS, + "Invalid bit precision r: '" + + std::to_string(r) + "'"); +} + PAlgebra::PAlgebra(long mm, long pp, const std::vector& _gens, @@ -519,8 +531,8 @@ PAlgebra::PAlgebra(long mm, Tidx.assign(mm, -1); // allocate m slots, initialize them to -1 zmsIdx.assign(mm, -1); // allocate m slots, initialize them to -1 resize(zmsRep, phiM); - long i, idx; - for (i = idx = 0; i < mm; i++) { + + for (long i = 0, idx = 0; i < mm; i++) { if (NTL::GCD(i, mm) == 1) { zmsIdx[i] = idx++; zmsRep[zmsIdx[i]] = i; @@ -539,8 +551,9 @@ PAlgebra::PAlgebra(long mm, // buffer is initialized to all-zero, which represents 1=\prod_i gi^0 std::vector buffer(gens.size()); // temporary holds exponents - i = idx = 0; + long ctr = 0; + long i = 0; do { ctr++; long t = exponentiate(buffer); diff --git a/src/PrimeGenerator.h b/src/PrimeGenerator.h new file mode 100644 index 000000000..08f2bb9ca --- /dev/null +++ b/src/PrimeGenerator.h @@ -0,0 +1,129 @@ +/* Copyright (C) 2012-2020 IBM Corp. + * This program is Licensed under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. See accompanying LICENSE file. + */ + +/* Intel HEXL integration and code reorg. + * Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// You initialize a PrimeGenerator as follows: +// PrimeGenerator gen(len, m); +// Each call to gen.next() generates a prime p with +// (1-1/2^B)*2^len <= p < 2^len and p = 2^k*t*m + 1, +// where t is odd and k is as large as possible +// and B is a small constant (typically, B in {2,3,4}). +// If no such prime is found, then an error is raised. + +#ifndef HELIB_PRIME_GENERATOR_H +#define HELIB_PRIME_GENERATOR_H + +#include "macro.h" // Private Header + +namespace helib { + +class PrimeGenerator +{ +private: + long len; + long m; + long k; + long t; + +public: + const static long B = 3; + + PrimeGenerator(long _len, long _m) : len(_len), m(_m) + { + assertInRange(len, + long(B), + static_cast(HELIB_SP_NBITS), + "PrimeGenerator: len is not " + "in [B, HELIB_SP_NBITS]", + true); + assertInRange(m, + 1l, + static_cast(NTL_SP_BOUND), + "PrimeGenerator: m is " + "not in [1, NTL_SP_BOUND)"); + + // compute k as smallest non-negative integer such that + // 2^{len-B} < 2^k*m + k = 0; + while ((m << k) <= (1L << (len - B))) + k++; + + t = divc((1L << len) - 1, m << k); + // this ensures the fist call to next will trigger a new k-value + } + + long next() + { + // we consider all odd t in the interval + // [ (1-1/2^B)*2^len-1)/(2^k*m), (2^len-1)/(2^k*m) ). + // For k satisfying 2^{len-B} >= 2^k*m, this interval is + // contains at least one integer. + // It is equivalent to consider the interval of integers + // [t_lower_bound, t_upper_bound), + // where t_lower_bound = ceil(((1-1/2^B)*2^len-1)/(2^k*m)) + // and t_upper_bound = ceil((2^len-1)/(2^k*m)). + + long t_upper_bound = divc((1L << len) - 1, m << k); + + for (;;) { + + t++; + + if (t >= t_upper_bound) { + // move to smaller value of k, reset t and t_upper_bound + + k--; + + long k_lower_bound = (m % 2 == 0) ? 0 : 1; + + // we run k down to 0 if m is even, and down to 1 + // if m is odd. + + if (k < k_lower_bound) + throw RuntimeError("Prime generator ran out of primes"); + + t = divc((1L << len) - (1L << (len - B)) - 1, m << k); + t_upper_bound = divc((1L << len) - 1, m << k); + } + + if (t % 2 == 0) + continue; // we only want to consider odd t + + long cand = ((t * m) << k) + 1; // = 2^k*t*m + 1 + + // double check that cand is in the prescribed interval + assertInRange(cand, + (1L << len) - (1L << (len - B)), + 1L << len, + "Candidate cand is not in the prescribed interval"); + + if (NTL::ProbPrime(cand, 60)) + return cand; + // iteration count == 60 implies 2^{-120} error probability + } + } +}; + +} // namespace helib +#endif // HELIB_PRIME_GENERATOR_H diff --git a/src/intelExt.cpp b/src/intelExt.cpp new file mode 100644 index 000000000..7db461882 --- /dev/null +++ b/src/intelExt.cpp @@ -0,0 +1,179 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/* + * Some simple wrappers to enable HElib to use HEXL. + */ + +#ifdef USE_INTEL_HEXL + +#include "intelExt.h" + +#include + +#include +#include +#include +#include + +namespace intel { + +using intel::hexl::NTT; + +static std::shared_mutex table_mutex; + +struct Key +{ + uint64_t degree; + uint64_t q; + + bool operator==(const Key& other) const + { + return this->degree == other.degree && this->q == other.q; + } +}; + +struct Hash +{ + size_t operator()(const Key& key) const + { + // Using the popular hash_combine method of boost. + return key.degree ^ + (key.q + 0x9e3779b9 + (key.degree << 6) + (key.degree >> 2)); + } +}; + +// Lookup table to avoid re-creating previously created NTTs +// For life of program. +static std::unordered_map table; + +static NTT& initNTT(uint64_t degree, uint64_t q) +{ + Key key = {degree, q}; + + { // First Read to see if there is an NTT + std::shared_lock read_lock(table_mutex); + auto it = table.find(key); + if (it != table.end()) { // Found + return it->second; + } + } + + { // Didn't find an NTT, lock the table for writing + std::scoped_lock write_lock(table_mutex); + // Check again to see another thread snuck it in. + auto it = table.find(key); + if (it != table.end()) { // Found + return it->second; + } else { + auto ret = table.emplace(key, NTT(degree, q)); + return (ret.first)->second; // The NTT object just created. + } + } +} + +void FFTFwd(long* output, const long* input, long n, long q) +{ + initNTT(/*degree=*/n, /*modulus=*/q) + .ComputeForward(reinterpret_cast(output), + reinterpret_cast(input), + /*input_mod_factor=*/1, + /*output_mod_factor=*/1); + return; +} + +void FFTRev1(long* output, const long* input, long n, long q) +{ + initNTT(/*degree=*/n, /*modulus=*/q) + .ComputeInverse(reinterpret_cast(output), + reinterpret_cast(input), + /*input_mod_factor=*/1, + /*output_mod_factor=*/1); + return; +} + +void EltwiseAddMod(long* result, + const long* operand1, + const long* operand2, + long n, + long modulus) +{ + intel::hexl::EltwiseAddMod(reinterpret_cast(result), + reinterpret_cast(operand1), + reinterpret_cast(operand2), + n, + modulus); +} + +void EltwiseAddMod(long* result, + const long* operand, + long scalar, + long n, + long modulus) +{ + intel::hexl::EltwiseAddMod(reinterpret_cast(result), + reinterpret_cast(operand), + scalar, + n, + modulus); +} + +void EltwiseSubMod(long* result, + const long* operand1, + const long* operand2, + long n, + long modulus) +{ + intel::hexl::EltwiseSubMod(reinterpret_cast(result), + reinterpret_cast(operand1), + reinterpret_cast(operand2), + n, + modulus); +} + +void EltwiseSubMod(long* result, + const long* operand, + long scalar, + long n, + long modulus) +{ + intel::hexl::EltwiseSubMod(reinterpret_cast(result), + reinterpret_cast(operand), + scalar, + n, + modulus); +} + +void EltwiseMultMod(long* result, + const long* operand1, + const long* operand2, + long n, + long modulus) +{ + intel::hexl::EltwiseMultMod(reinterpret_cast(result), + reinterpret_cast(operand1), + reinterpret_cast(operand2), + n, + modulus, + /*input_mod_factor=*/1); +} + +void EltwiseMultMod(long* result, + const long* operand, + long scalar, + long n, + long modulus) +{ + + intel::hexl::EltwiseFMAMod(reinterpret_cast(result), + reinterpret_cast(operand), + scalar, + nullptr, + n, + modulus, + /*input_mod_factor=*/1); +} + +} // namespace intel + +#endif // USE_INTEL_HEXL diff --git a/src/intelExt.h b/src/intelExt.h new file mode 100644 index 000000000..9098afb62 --- /dev/null +++ b/src/intelExt.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HELIB_INTELEXT_H +#define HELIB_INTELEXT_H + +#include + +namespace intel { + +// Simple wrappers around HEXL +// void FFTFwd(long* output, const long* input, long n, long q, long root); +// void FFTRev1(long* output, const long* input, long n, long q, long root); +void FFTFwd(long* output, const long* input, long n, long q); +void FFTRev1(long* output, const long* input, long n, long q); + +void EltwiseAddMod(long* result, + const long* operand1, + const long* operand2, + long n, + long modulus); +void EltwiseAddMod(long* result, + const long* operand, + long scalar, + long n, + long modulus); + +void EltwiseSubMod(long* result, + const long* operand1, + const long* operand2, + long n, + long modulus); +void EltwiseSubMod(long* result, + const long* operand, + long scalar, + long n, + long modulus); + +void EltwiseMultMod(long* result, + const long* operand1, + const long* operand2, + long n, + long modulus); +void EltwiseMultMod(long* result, + const long* operand, + long scalar, + long n, + long modulus); + +} // namespace intel + +#endif // HELIB_INTELEXT_H diff --git a/src/macro.h b/src/macro.h new file mode 100644 index 000000000..f4fc77ee2 --- /dev/null +++ b/src/macro.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HELIB_MACRO_H +#define HELIB_MACRO_H + +// HEXL works best with primes of fewer than 50 bits. +#ifndef HELIB_SP_NBITS +#ifdef USE_INTEL_HEXL +#define HELIB_SP_NBITS (49) +#else +#define HELIB_SP_NBITS NTL_SP_NBITS +#endif // USE_INTEL_HEXL +#endif // HELIB_SP_NBITS + +#endif // HELIB_MACRO_H diff --git a/src/matmul.cpp b/src/matmul.cpp index 8952c9d83..06eef33de 100644 --- a/src/matmul.cpp +++ b/src/matmul.cpp @@ -103,7 +103,7 @@ class BasicAutomorphPrecon HELIB_STATS_UPDATE("KS-noise-ratio-hoist", ratio); if (ratio > 1) { - Warning("KS-noise-ratio-hoist=" + std::to_string(ratio) + "\n"); + Warning("KS-noise-ratio-hoist=" + std::to_string(ratio)); } // std::stderr << "*** HOIST INIT\n"; // fprintf(stderr, " KS-log-noise-ratio-hoist: %f\n", diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index beac44f07..e0bf08879 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,7 @@ if (NOT ONLY_ADD_TEST) "TestContext.cpp" "TestCtxt.cpp" "TestErrorHandling.cpp" + "TestHEXL.cpp" "TestLogging.cpp" "TestMatmulCKKS.cpp" "TestMatrix.cpp" @@ -149,6 +150,7 @@ set(TEST_NAMES "TestCtxt" "TestErrorHandling" "TestFatBootstrappingWithMultiplications" + "TestHEXL" "TestLogging" "TestMatmulCKKS" "TestMatrix" diff --git a/tests/GTestGeneral.cpp b/tests/GTestGeneral.cpp index 2dc2add5f..12ea8abd3 100644 --- a/tests/GTestGeneral.cpp +++ b/tests/GTestGeneral.cpp @@ -522,6 +522,7 @@ TEST_P(GTestGeneral, rotate1D) EXPECT_TRUE(ciphertextMatches(ea, secretKey, p0, c0)); long pos = 5; // Position of element to replicate + ASSERT_LE(pos, ea.size()); ea.encrypt(c1, publicKey, p1); helib::Ctxt c1r = c1; @@ -544,6 +545,7 @@ TEST_P(GTestGeneral, rotate1D) INSTANTIATE_TEST_SUITE_P(variousParameters, GTestGeneral, ::testing::Values( // R, p, r, d, c, k, L, s, m, mvec, gens, ords, seed, nt //DEEP + Parameters(1, 127, 1, 2, 2, 10, 500, 0, 64, {}, {}, {}, 0, 1), Parameters(1, 2, 2, 1, 2, 10, 500, 0, 91, {}, {}, {}, 0, 1), Parameters(1, 2, 1, 2, 2, 10, 500, 0, 91, {}, {}, {}, 0, 1), Parameters(2, 7, 2, 1, 2, 10, 500, 0, 91, {}, {}, {}, 0, 1), diff --git a/tests/TestHEXL.cpp b/tests/TestHEXL.cpp new file mode 100644 index 000000000..e0e63a856 --- /dev/null +++ b/tests/TestHEXL.cpp @@ -0,0 +1,247 @@ +/* Copyright (C) 2021 Intel Corporation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test_common.h" + +#include "../src/macro.h" // Private header +// only run if HEXL has been linked. +#ifdef USE_INTEL_HEXL +#include "../src/intelExt.h" // Private header +#include "../src/PrimeGenerator.h" // Private header + +namespace { + +// FIXME Copied from GTestGeneral. Should really have common functionality. +::testing::AssertionResult ciphertextMatches(const helib::EncryptedArray& ea, + const helib::SecKey& sk, + const helib::PtxtArray& p, + const helib::Ctxt& c) +{ + helib::PtxtArray pp(ea); + pp.decrypt(c, sk); + if (pp == p) { + return ::testing::AssertionSuccess(); + } else { + return ::testing::AssertionFailure() + << "Ciphertext does not match plaintext:" << std::endl + << "p = " << p << std::endl + << "pp = " << pp << std::endl; + } +} + +struct Parameters +{ + Parameters(unsigned m, + unsigned p, + unsigned r, + unsigned bits, + const std::vector& gens = {}, + const std::vector& ords = {}) : + m(m), p(p), r(r), bits(bits), gens(gens), ords(ords){}; + + const unsigned m; + const unsigned p; + const unsigned r; + const unsigned bits; + const std::vector gens; + const std::vector ords; + + friend std::ostream& operator<<(std::ostream& os, const Parameters& params) + { + return os << "{" + << "m = " << params.m << ", " + << "p = " << params.p << ", " + << "r = " << params.r << ", " + << "gens = " << helib::vecToStr(params.gens) << ", " + << "ords = " << helib::vecToStr(params.ords) << ", " + << "bits = " << params.bits << "}"; + } +}; + +class TestHEXL_BGV : public ::testing::TestWithParam +{ +protected: + const unsigned long m; + const unsigned long p; + const unsigned long r; + const unsigned long bits; + helib::Context context; + helib::SecKey secretKey; + const helib::PubKey publicKey; + const helib::EncryptedArray& ea; + + TestHEXL_BGV() : + m(GetParam().m), + p(GetParam().p), + r(GetParam().r), + bits(GetParam().bits), + context(helib::ContextBuilder() + .m(m) + .p(p) + .r(r) + .bits(bits) + .build()), + secretKey(context), + publicKey((secretKey.GenSecKey(), + helib::addSome1DMatrices(secretKey), + secretKey)), + ea(context.getEA()) + {} + + virtual void SetUp() override + { + NTL::SetNumThreads(1); + if (helib_test::verbose) { + ea.getPAlgebra().printout(); + std::cout << "r = " << context.getAlMod().getR() << std::endl; + std::cout << "ctxtPrimes=" << context.getCtxtPrimes() + << ", specialPrimes=" << context.getSpecialPrimes() << "\n" + << std::endl; + } + + helib::setupDebugGlobals(&secretKey, context.shareEA()); + } + + virtual void TearDown() override { helib::cleanupDebugGlobals(); } +}; + +struct HEXL_params +{ + const long N; // phim + const long modulus; + HEXL_params(long _N, long _modulus) : N(_N), modulus(_modulus) {} +}; + +class TestHEXL : public ::testing::TestWithParam +{ +protected: + const long N; // phim + const long modulus; + + TestHEXL() : N(GetParam().N), modulus(GetParam().modulus) {} +}; + +TEST(TestHEXL, hexlInUse) +{ + // This test does not use HEXL_params because algebra must be d == 1 + + long N = 64; + long modulus = 769; + + std::vector args(N); + for (size_t i = 0; i < args.size(); ++i) { + args[i] = i + 1; + } + auto expected_outputs = args; + + intel::FFTFwd(args.data(), args.data(), N, modulus); + intel::FFTRev1(args.data(), args.data(), N, modulus); + + EXPECT_TRUE(std::equal(args.begin(), args.end(), expected_outputs.begin())); +} + +TEST_P(TestHEXL_BGV, multiplyTwoCtxts) +{ + helib::PtxtArray p0(ea), p1(ea); + p0.random(); + p1.random(); + + helib::Ctxt c0(publicKey), c1(publicKey); + p0.encrypt(c0); + p1.encrypt(c1); + + p0 *= p1; + c0 *= c1; + + EXPECT_TRUE(ciphertextMatches(ea, secretKey, p0, c0)); +} + +TEST_P(TestHEXL_BGV, encryptDecrypt) +{ + NTL::SetNumThreads(1); + helib::PtxtArray p0(ea); + + std::vector v0 = {0, 5}; + + p0.load(v0); + + helib::Ctxt c0(publicKey); + p0.encrypt(c0); + + EXPECT_TRUE(ciphertextMatches(ea, secretKey, p0, c0)); +} + +TEST_P(TestHEXL, CModulusFFT) +{ + NTL::SetNumThreads(1); + + long m = 2 * N; + helib::PAlgebra zms(m, modulus); + + std::cout << "***SP bits: " << HELIB_SP_NBITS << '\n'; + + helib::PrimeGenerator prime_generator(HELIB_SP_NBITS, m); + long q = prime_generator.next(); + helib::Cmodulus cmod(zms, q, 0); + + NTL::ZZX poly; + poly.SetLength(2); + poly[0] = 0; + poly[1] = 5; + // std::cout << "TEST: input " << poly << std::endl; + + NTL::vec_long transformed; + cmod.FFT(transformed, poly); + // std::cout << "TEST: transformed " << transformed << std::endl; + + NTL::zz_pX inverse; + cmod.iFFT(inverse, transformed); + // std::cout << "TEST: inverse " << inverse << std::endl; + + NTL::ZZX inverse_conv = NTL::conv(inverse); + EXPECT_EQ(inverse_conv, poly); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(typicalParameters, TestHEXL_BGV, ::testing::Values( + //Parameters(16, 3, 1, 300) // m power of 2. + Parameters(8, 769, 1, 50) // m power of 2. + )); + +INSTANTIATE_TEST_SUITE_P(typicalParameters, TestHEXL, ::testing::Values( + //Parameters(16, 3, 1, 300) // m power of 2. + HEXL_params(/*phim=*/8, /*p=*/769), // m power of 2, d == 1. + HEXL_params(/*phim=*/64, /*p=*/769), // m power of 2, d == 1. + HEXL_params(/*m=512, phim=*/256, /*p=*/769) // m power of 2, d == 2. + )); +// clang-format on + +} // namespace + +#else + +namespace { + +TEST(TestHEXL, noTestRequired) +{ + std::cout << "***SP bits: " << HELIB_SP_NBITS << '\n'; +} + +} // namespace + +#endif // USE_INTEL_HEXL diff --git a/tests/TestPermutations.cpp b/tests/TestPermutations.cpp index 274d8d7b9..86ea027f8 100644 --- a/tests/TestPermutations.cpp +++ b/tests/TestPermutations.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "../src/macro.h" #include "gtest/gtest.h" #include "test_common.h" @@ -74,8 +75,8 @@ struct BGVParameters struct CKKSParameters : public BGVParameters { - CKKSParameters(long m, long r, long bits, long depth) : - BGVParameters(m, /*p=*/-1, r, bits, depth){}; + CKKSParameters(long m, long precision, long bits, long depth) : + BGVParameters(m, /*p=*/-1, /*r=*/precision, bits, depth){}; }; class TestPermutationsGeneral : public ::testing::TestWithParam @@ -256,6 +257,15 @@ TEST_P(TestPermutationsBGV, ciphertextPermutationsWithNewAPI) EXPECT_EQ(w, v); } +// This test is in TestPermutations for now as this is where +// this issue was discovered. +TEST(TestPermutationsCKKS, ckksFailIfRBitsTooLarge) +{ + EXPECT_THROW( + helib::ContextBuilder().precision(HELIB_SP_NBITS).build(), + helib::InvalidArgument); +} + TEST_P(TestPermutationsCKKS, ciphertextPermutationsWithNewAPI) { helib::PermIndepPrecomp pip(context, depth); @@ -329,12 +339,15 @@ INSTANTIATE_TEST_SUITE_P(variousParameters, /*bits=*/1000, /*depth=*/5))); -INSTANTIATE_TEST_SUITE_P(variousParameters, - TestPermutationsCKKS, - ::testing::Values(CKKSParameters(/*m=*/8192, - /*r=*/50, - /*bits=*/1000, - /*depth=*/5))); +INSTANTIATE_TEST_SUITE_P( + variousParameters, + TestPermutationsCKKS, + ::testing::Values(CKKSParameters(/*m=*/8192, + /*precision=*/HELIB_SP_NBITS > 52 + ? 52 + : HELIB_SP_NBITS - 1, + /*bits=*/1000, + /*depth=*/5))); INSTANTIATE_TEST_SUITE_P( variousParameters, diff --git a/tests/test_common.cpp b/tests/test_common.cpp index 5a67491bf..be6c0967e 100644 --- a/tests/test_common.cpp +++ b/tests/test_common.cpp @@ -56,7 +56,7 @@ static inline long howManyThreads(long max = 0) ") cannot be less than zero." " Zero means use the maximum available on system."); - // Get system threads and clip to max + // Get system threads and clip to max #ifdef HELIB_THREADS long threads = std::thread::hardware_concurrency(); #else From 11f516c1f5c940f6fece4e6305bb8e49775a712d Mon Sep 17 00:00:00 2001 From: Jack Crawford Date: Wed, 1 Sep 2021 21:54:46 +0100 Subject: [PATCH 2/5] HEXL minimum version 1.2.1 * Enforce minimum version of Intel HEXL * Change HEXL minimum version to 1.2.1 Co-authored-by: Flavio Bergamaschi --- CMakeLists.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ff8b8c7a..0b9bfdb2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,7 +209,19 @@ if(USE_INTEL_HEXL) ) set(INIT_HEXL_DIR ${HEXL_DIR}) endif() - find_package(HEXL 1.2.1 HINTS ${HEXL_DIR} REQUIRED) + + # Set minimum version of HEXL + set(HEXL_MIN_VERSION 1.2.1) + + find_package(HEXL HINTS ${HEXL_DIR} REQUIRED) + + # Check the version found is greater than the minimum + if(HEXL_MIN_VERSION GREATER HEXL_VERSION) + message(FATAL_ERROR "Minimum Intel HEXL version required is ${HEXL_MIN_VERSION}, found ${HEXL_VERSION}") + else() + message("-- Intel HEXL Version: ${HEXL_VERSION} found (minimum required is ${HEXL_MIN_VERSION})") + endif() + if(INIT_HEXL_DIR AND NOT INIT_HEXL_DIR STREQUAL HEXL_DIR) message(WARNING "HEXL location provided '${INIT_HEXL_DIR}' \ does not equal chosen '${HEXL_DIR}'.") From 6fba4f8ccca4d9e047afaaeb188675e27fa721fa Mon Sep 17 00:00:00 2001 From: Flavio Bergamaschi Date: Mon, 6 Sep 2021 14:58:56 +0100 Subject: [PATCH 3/5] Update build to use NTL 11.5.1 and GMP 6.2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update package build to NTL 11.5.1 and GMP 6.2.1 * Update INSTALL.md Update to GMP 6.2.1 and NTL 11.5.1 Additional instructions for Intel® HEXL Added note about HElib with HEXL acceleration Co-authored-by: Jack Crawford --- CHANGES.md | 2 + CMakeLists.txt | 6 +-- Dockerfile | 21 ---------- INSTALL.md | 42 ++++++++++--------- dependencies/gmp/CMakeLists.txt | 2 +- dependencies/ntl/CMakeLists.txt | 4 +- docker/build_and_test/Dockerfile.FEDORA | 4 +- .../build_scripts/build_and_test_helib.sh | 4 +- 8 files changed, 34 insertions(+), 51 deletions(-) delete mode 100644 Dockerfile diff --git a/CHANGES.md b/CHANGES.md index 46f941611..734e82883 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,8 @@ HElib 2.2.0, September 2021 * HEXL NTT acceleration for m as a power of 2 * HEXL acceleration to fundamental ciphertext operations * Documentation update +* Update NTL dependency to version 11.5.1 +* Update GMP dependency to version 6.2.1 * Update to benchmarks * Bug fixes diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b9bfdb2f..8b35650f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,9 +72,9 @@ set(HELIB_DEPENDENCIES_DIR "${PROJECT_SOURCE_DIR}/dependencies") set(HELIB_TEST_BIN_DIR "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") # GMP minimal version to be used if not PACKAGE_BUILD -set(GMP_MINIMAL_VERSION "6.0.0") +set(GMP_MINIMAL_VERSION "6.2.0") # NTL minimal version to be used if NOT PACKAGE_BUILD -set(NTL_MINIMAL_VERSION "11.0.0") +set(NTL_MINIMAL_VERSION "11.4.3") # The -isysroot flag needs set. if ((APPLE OR (CMAKE_CXX_PLATFORM_ID STREQUAL "Darwin")) AND @@ -238,7 +238,7 @@ if (PACKAGE_BUILD) # GMP version to be used (and eventually downloaded) if PACKAGE_BUILD set(FETCHED_GMP_VERSION "6.2.1") # NTL version to be used (and eventually downloaded) if PACKAGE_BUILD - set(FETCHED_NTL_VERSION "11.4.3") + set(FETCHED_NTL_VERSION "11.5.1") # Setting up default compilation output directory if (NOT PACKAGE_DIR) set(PACKAGE_DIR "helib_pack") diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 6bf54f072..000000000 --- a/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ubuntu:18.04 - -COPY ./ /root/HElib/ - -RUN apt update && \ - apt install -y build-essential wget git cmake m4 libgmp-dev file && \ - cd ~ && \ - wget https://www.shoup.net/ntl/ntl-11.4.1.tar.gz && \ - tar --no-same-owner -xf ntl-11.4.1.tar.gz && \ - cd ntl-11.4.1/src && \ - ./configure SHARED=on NTL_GMP_LIP=on NTL_THREADS=on NTL_THREAD_BOOST=on && \ - make -j4 && \ - make install && \ - cd ~ && \ - mkdir HElib/build && \ - cd HElib/build && \ - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED=ON -DENABLE_THREADS=ON .. && \ - make -j4 && \ - make install && \ - ldconfig - diff --git a/INSTALL.md b/INSTALL.md index b882fb3e5..d578c84c4 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,45 +1,45 @@ # Building and installing HElib The current HElib build, install, and regression tests suite have been built -and tested on Ubuntu 20.04 and macOS Catalina ->=10.15.7. Previous versions have also included Ubuntu 18.04, Fedora 33, CentOS -8.2, macOS Mojave >=10.14.6. +and tested on Ubuntu 20.04 and macOS Catalina >=10.15.7. Previous versions +have also included Ubuntu 18.04, Fedora 33, CentOS 8.2, macOS Mojave >=10.14.6. There are two different ways to build and install HElib. The first one will automatically download and build the GMP and NTL dependencies and pack the libraries in a relocatable folder. The second way, instead, requires the dependencies to be installed by you and available in the system. -Intel [HEXL](https://github.com/intel/hexl) acceleration library for -homomorphic encryption support has been added. This should be treated as -experimental. Instructions to enable HEXL and link to it are given +This release of HElib has experimental support for the Intel® [HEXL](https://github.com/intel/hexl) +acceleration library for homomorphic encryption that exploits Intel® Advanced Vector Extensions 512. +Instructions to enable and link to HEXL are given [below](#enabling-and-linking-to-intel-hexl). -**Please read these instructions in full to better choose the type of build that - is best for you.** +```diff +- Please read these instructions in full to better choose the type of build that is best for you. +``` ## General prerequisites - pthreads - git >= 2.27 (required to build and run the HElib test suite) -**Linux environment:** +**Default Linux environment:** - GNU make >= 4.2 -- g++ >= 9.3.0 +- g++ >= 9.3.0 (recommended g++ 10.3.0) - cmake >= 3.16 **macOS environment:** - Apple clang >= 12.0.0 (available with the latest Xcode for the tested versions of macOS) - Xcode Command Line Tools (can be installed with the command `xcode-select - --install` in a teminal) + --install` in a terminal) - cmake >= 3.20 (available from [CMake](https://cmake.org/) or [MacPorts Project](https://www.macports.org/) and [Homebrew](https://brew.sh/) as packages) - GNU make >= 3.81 -**For development:** +**For HElib development:** - clang-format >= 9.0.0 (available with your linux distribution and for macOS from [MacPorts Project](https://www.macports.org/) and @@ -115,7 +115,7 @@ some other system-wide path, step 5 may require `sudo` privileges. This option involves building HElib on its own, linking against pre-existing dependencies (NTL and GMP) on the system. In this way, the HElib library can be moved around, but its dependencies (NTL and GMP) cannot, as they are absolute -paths. For this option, you must build GMP >=6.0.0 and NTL >=11.4.3 yourself. +paths. For this option, you must build GMP >=6.2.1 and NTL >=11.5.1 yourself. For details on how to do this, please see the section on building dependencies later. It is assumed throughout this installation option that the environment variables `$GMPDIR` and `$NTLDIR` are set to point to the installation @@ -177,8 +177,8 @@ Many distributions come with GMP pre-installed. If not, you can install GMP as follows. 1. Download GMP from [http://www.gmplib.org](http://www.gmplib.org) -- make sure - that you get GMP >=6.0.0 (current version is 6.2.0). -2. Decompress and cd into the gmp directory (e.g., `gmp-6.2.0`). + that you get GMP >=6.2.0 (current version is 6.2.1). +2. Decompress and cd into the gmp directory (e.g., `gmp-6.2.1`). 3. GMP is compiled in the standard unix way: ```bash @@ -196,9 +196,9 @@ step 3. You can install NTL as follows: -1. Download NTL >=11.4.3 (current version is 11.4.3) from - [http://www.shoup.net/ntl/download.html](http://www.shoup.net/ntl/download.html) -2. Decompress and cd into the directory, e.g., `ntl-11.4.3/src` +1. Download NTL >=11.5.1 from + [https://libntl.org/download.html](https://libntl.org/download.html) +2. Decompress and cd into the directory, e.g., `ntl-11.5.1/src` 3. NTL is configured, built and installed in the standard Unix way (but remember to specify the following flags to `configure`): @@ -216,14 +216,16 @@ step 3. **NOTE**: if linking against a non-system GMP, pass `GMP_PREFIX=` to the `./configure` step. -## Enabling and linking to Intel HEXL +## Enabling and linking to Intel® HEXL +**NOTE:** HElib with HEXL acceleration is only supported on the processors with AVX512DQ and +AVX512-IFMA such as the 3rd generation Intel® Xeon® or the 11th generation Intel® Core® **NOTE:** It is currently only possible to use HEXL with HElib when using the library build and when building HElib as a static library. i.e. `-DPACKAGE_BUILD=OFF` and `-DBUILD_SHARED=OFF`. First you must download and build HEXL from source. Currently, HElib only -works with HEXL version 1.2. Using git this would be +works with HEXL version >= 1.2.1 Using git this would be ```bash git clone https://github.com/intel/hexl --branch 1.2.1 diff --git a/dependencies/gmp/CMakeLists.txt b/dependencies/gmp/CMakeLists.txt index 337686f9c..1149d9171 100644 --- a/dependencies/gmp/CMakeLists.txt +++ b/dependencies/gmp/CMakeLists.txt @@ -28,7 +28,7 @@ ExternalProject_Add( URL "https://gmplib.org/download/gmp/gmp-${FETCHED_GMP_VERSION}.tar.bz2" "https://ftp.gnu.org/gnu/gmp/gmp-${FETCHED_GMP_VERSION}.tar.bz2" URL_HASH - # NOTE: hash of version 6.2.0 + # NOTE: hash of version 6.2.x SHA256=eae9326beb4158c386e39a356818031bd28f3124cf915f8c5b1dc4c7a36b4d7c DOWNLOAD_NO_PROGRESS 1 UPDATE_COMMAND "" diff --git a/dependencies/ntl/CMakeLists.txt b/dependencies/ntl/CMakeLists.txt index 886e98ce2..72a80bb31 100644 --- a/dependencies/ntl/CMakeLists.txt +++ b/dependencies/ntl/CMakeLists.txt @@ -62,8 +62,8 @@ ExternalProject_Add( URL "https://libntl.org/ntl-${FETCHED_NTL_VERSION}.tar.gz" "https://www.shoup.net/ntl/ntl-${FETCHED_NTL_VERSION}.tar.gz" URL_HASH - # NOTE: hash of version 11.4.3 - SHA256=b7c1ccdc64840e6a24351eb4a1e68887d29974f03073a1941c906562c0b83ad2 + # NOTE: hash of version 11.5.1 + SHA256=210d06c31306cbc6eaf6814453c56c776d9d8e8df36d74eb306f6a523d1c6a8a DOWNLOAD_NO_PROGRESS ON UPDATE_COMMAND "" # Turn LOG_CONFIGURE ON will log the configure step to a file in the stamp directory diff --git a/docker/build_and_test/Dockerfile.FEDORA b/docker/build_and_test/Dockerfile.FEDORA index ddbab8c9f..b58c62000 100644 --- a/docker/build_and_test/Dockerfile.FEDORA +++ b/docker/build_and_test/Dockerfile.FEDORA @@ -1,5 +1,5 @@ -# Recipe for an HE-ready Fedora 33 docker -FROM fedora:33 +# Recipe for an HE-ready Fedora 34 docker +FROM fedora:34 # Install general HElib pre-requisites RUN yum -y upgrade && \ diff --git a/docker/build_and_test/build_scripts/build_and_test_helib.sh b/docker/build_and_test/build_scripts/build_and_test_helib.sh index ddbea5f07..5cd6e5572 100755 --- a/docker/build_and_test/build_scripts/build_and_test_helib.sh +++ b/docker/build_and_test/build_scripts/build_and_test_helib.sh @@ -1,11 +1,11 @@ #!/bin/bash # Script for building and testing HElib in various configurations. -# Default behaviour is build IBM-HElib/HElib master using the package build +# Default behaviour is build homenc/HElib master using the package build # without running any tests. # Default arguments -repo=https://github.com/IBM-HElib/HElib.git +repo=https://github.com/homenc/HElib.git branch=master package=true tests=false From e5bd05c0ebb154fd2212d3060d9873ef58c8b024 Mon Sep 17 00:00:00 2001 From: Flavio Bergamaschi Date: Tue, 7 Sep 2021 13:21:57 +0100 Subject: [PATCH 4/5] Update benchmark/README.md --- benchmarks/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/README.md b/benchmarks/README.md index ef2099311..5ca2a0a3f 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -9,10 +9,10 @@ Build and install HElib as described in [INSTALL.md](../INSTALL.md). ### Build and install google benchmark Download the library https://github.com/google/benchmark from github, build and -then install it. +then install it. We tested with Google Benchmark v1.5.6 ``` -git clone https://github.com/google/benchmark +git clone https://github.com/google/benchmark.git -b v1.5.6 cd benchmark mkdir build cd build From 0f21f50d050f78c4ebd4c31a49bf26cbe1cc3a91 Mon Sep 17 00:00:00 2001 From: Flavio Bergamaschi Date: Tue, 7 Sep 2021 18:45:52 +0100 Subject: [PATCH 5/5] Update INSTALL.md --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index d578c84c4..4969edc46 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -238,7 +238,7 @@ created by default. For a quick start most people will want, ```bash cd hexl -cmake -S . -B build/ [-DCMAKE_INSTALL_PREFIX=] +cmake -S . -B build/ [-DCMAKE_INSTALL_PREFIX=/lib/cmake/hexl-1.2.1] cmake --build build -j [] cmake --install build ```