diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index e34c76dca..8d8874552 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -34,6 +34,7 @@ jobs: run: shell: bash strategy: + fail-fast: false matrix: # TODO: shared builds package_build: [ON, OFF] @@ -50,6 +51,10 @@ jobs: hexl: ON steps: - uses: actions/checkout@v2 + - name: System Check + run: | + clang --version + gcc -v - run: | set -x env diff --git a/src/EncryptedArray.cpp b/src/EncryptedArray.cpp index 1d551a97b..692761ccf 100644 --- a/src/EncryptedArray.cpp +++ b/src/EncryptedArray.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2012-2020 IBM Corp. +/* Copyright (C) 2012-2022 Intel 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 @@ -707,32 +707,32 @@ void runningSums(const EncryptedArray& ea, Ctxt& ctxt) void totalSums(const EncryptedArray& ea, Ctxt& ctxt) { - long n = ea.size(); + long n = ea.size(); // slot-count if (n == 1) return; Ctxt orig = ctxt; - - long k = NTL::NumBits(n); + + long b = NTL::NumBits(n); long e = 1; - for (long i = k - 2; i >= 0; i--) { - Ctxt tmp1 = ctxt; - ea.rotate(tmp1, e); - ctxt += tmp1; // ctxt = ctxt + (ctxt >>> e) + for (long i = b - 2; i >= 0; i--) { + + Ctxt tmp1 = orig; + helib::rotate(tmp1, e); + orig += tmp1; e = 2 * e; if (NTL::bit(n, i)) { Ctxt tmp2 = orig; - ea.rotate(tmp2, e); - ctxt += tmp2; // ctxt = ctxt + (orig >>> e) - // NOTE: we could have also computed - // ctxt = (ctxt >>> e) + orig, however, - // this would give us greater depth/noise + helib::rotate(tmp2, 1); + tmp2 += ctxt; + orig = tmp2; e += 1; } } + ctxt = orig; } // Linearized polynomials. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e0bf08879..e0b52243b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,6 +30,7 @@ if (NOT ONLY_ADD_TEST) "TestClonedPtr.cpp" "TestContext.cpp" "TestCtxt.cpp" + "TestEncryptedArray.cpp" "TestErrorHandling.cpp" "TestHEXL.cpp" "TestLogging.cpp" @@ -148,6 +149,7 @@ set(TEST_NAMES "TestClonedPtr" "TestContext" "TestCtxt" + "TestEncryptedArray" "TestErrorHandling" "TestFatBootstrappingWithMultiplications" "TestHEXL" diff --git a/tests/TestEncryptedArray.cpp b/tests/TestEncryptedArray.cpp new file mode 100644 index 000000000..3f1f90b5b --- /dev/null +++ b/tests/TestEncryptedArray.cpp @@ -0,0 +1,324 @@ +/* Copyright (C) 2022 Intel Corporation + * 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. + */ + +#include +#include + +#include "test_common.h" +#include "gtest/gtest.h" + +namespace { + +struct BGVParameters +{ + const long m; + const long p; + const long r; + const long bits; + + BGVParameters(long m, + long p, + long r, + long bits) : m(m), p(p), r(r), bits(bits){}; + + friend std::ostream& operator<<(std::ostream& os, const BGVParameters& params) + { + return os << "{" + << "m = " << params.m << ", " + << "p = " << params.p << ", " + << "r = " << params.r << ", " + << "bits = " << params.bits << "}"; + } +}; + +struct CKKSParameters +{ + const long m; + const long precision; + const long bits; + + CKKSParameters(long m, long precision, long bits) : + m(m), precision(precision), bits(bits){}; + + friend std::ostream& operator<<(std::ostream& os, + const CKKSParameters& params) + { + return os << "{" + << "m = " << params.m << ", " + << "precision = " << params.precision << ", " + << "bits = " << params.bits << "}"; + } +}; + +class TestTotalSums_BGV : public ::testing::TestWithParam +{ +protected: + const long m; + const long p; + const long r; + const long bits; + + helib::Context context; + helib::SecKey secretKey; + helib::PubKey publicKey; + const helib::EncryptedArray& ea; + + TestTotalSums_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(), addSome1DMatrices(secretKey), secretKey)), + ea(context.getEA()) + {} + + virtual void SetUp() override + { + helib::setupDebugGlobals(&secretKey, context.shareEA()); + } + + virtual void TearDown() override { helib::cleanupDebugGlobals(); } +}; + + +class TestTotalSums_CKKS : public ::testing::TestWithParam +{ +protected: + const long m; + const long precision; + const long bits; + + helib::Context context; + helib::SecKey secretKey; + helib::PubKey publicKey; + const helib::EncryptedArray& ea; + + TestTotalSums_CKKS() : + m(GetParam().m), + precision(GetParam().precision), + bits(GetParam().bits), + context(helib::ContextBuilder() + .m(m) + .precision(precision) + .bits(bits) + .build()), + secretKey(context), + publicKey( + (secretKey.GenSecKey(), addSome1DMatrices(secretKey), secretKey)), + ea(context.getEA()) + {} + + virtual void SetUp() override + { + helib::setupDebugGlobals(&secretKey, context.shareEA()); + } + + virtual void TearDown() override { helib::cleanupDebugGlobals(); } +}; + +//tests here + +TEST_P(TestTotalSums_BGV, TSumsWorkCorrForPosValBGV) +{ + std::vector data(context.getEA().size()); + std::iota(data.begin(), data.end(), 1); + + helib::Ptxt ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + + for (std::size_t i = 0; i < ptxt.size(); ++i) { + EXPECT_EQ(post_decryption[i], ptxt[i]); + } +} + +TEST_P(TestTotalSums_BGV, TSumsWorkCorrForNegValBGV) +{ + std::vector data(context.getEA().size()); + std::iota(data.begin(), data.end(), -context.getEA().size()); + + helib::Ptxt ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + + for (std::size_t i = 0; i < ptxt.size(); ++i) { + EXPECT_EQ(post_decryption[i], ptxt[i]); + } +} + +TEST_P(TestTotalSums_BGV, TSumsWorkCorrForPosNegValBGV) +{ + std::vector data(context.getEA().size()); + + for (std::size_t i=0; i ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + for (std::size_t i = 0; i < ptxt.size(); ++i) { + EXPECT_EQ(post_decryption[i], ptxt[i]); + } +} + +TEST_P(TestTotalSums_BGV, TSumsWorkCorrForZeroValBGV) +{ + std::vector data(context.getEA().size(), 0); + + helib::Ptxt ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + for (std::size_t i = 0; i < ptxt.size(); ++i) { + EXPECT_EQ(post_decryption[i], ptxt[i]); + } +} + +TEST_P(TestTotalSums_CKKS, TSumsWorkCorrForZeroValCKKS) +{ + std::vector> data(context.getEA().size(), 0.0); + + helib::Ptxt ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + COMPARE_CXDOUBLE_VECS(ptxt.getSlotRepr(), post_decryption.getSlotRepr()); +} + +TEST_P(TestTotalSums_CKKS, TSumsWorkCorrForPosValCKKS) +{ + std::vector> data(context.getEA().size()); + for (std::size_t i = 0; i < data.size(); ++i) { + data[i] = {i / 1.0, (i * i) / 1.0}; + } + + helib::Ptxt ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + COMPARE_CXDOUBLE_VECS(ptxt.getSlotRepr(), post_decryption.getSlotRepr()); +} + +TEST_P(TestTotalSums_CKKS, TSumsWorkCorrForNegValCKKS) +{ + std::vector> data(context.getEA().size()); + for (std::size_t i = 0; i < data.size(); ++i) { + data[i] = {-i / 1.0, -(i * i) / 1.0}; + } + + helib::Ptxt ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + COMPARE_CXDOUBLE_VECS(ptxt.getSlotRepr(), post_decryption.getSlotRepr()); +} + +TEST_P(TestTotalSums_CKKS, TSumsWorkCorrForPosNegValCKKS) +{ + std::vector> data(context.getEA().size()); + for (std::size_t i = 0; i < data.size(); ++i) { + if(i%2 == 0){ + data[i] = {-i / 1.0, -(i * i) / 1.0}; + } + else{ + data[i] = {i / 1.0, (i * i) / 1.0}; + } + } + + helib::Ptxt ptxt(context, data); + ptxt.totalSums(); + + helib::Ctxt ctxt(publicKey); + publicKey.Encrypt(ctxt, ptxt); + totalSums(context.getEA(), ctxt); + + helib::Ptxt post_decryption(context); + secretKey.Decrypt(post_decryption, ctxt); + + COMPARE_CXDOUBLE_VECS(ptxt.getSlotRepr(), post_decryption.getSlotRepr()); +} + + +// parameters initialization +INSTANTIATE_TEST_SUITE_P(Parameters, + TestTotalSums_BGV, + ::testing::Values(BGVParameters(/*m=*/45, + /*p=*/317, + /*r=*/1, + /*bits=*/500), BGVParameters(512, /*fermat_prime=*/257, 1, 500), BGVParameters(45, 317, 1, 500), BGVParameters(288, /*fermat_prime=*/17, 1, 500), BGVParameters(45, 367, 1, 500))); + +INSTANTIATE_TEST_SUITE_P(Parameters, + TestTotalSums_CKKS, + ::testing::Values(CKKSParameters(/*m=*/64, + /*precision=*/30, + /*bits=*/500), CKKSParameters(128, 35, 500), CKKSParameters(256, 40, 500), CKKSParameters(512, 50, 500), CKKSParameters(1024, 45, 500))); + +} // namespace