Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving rotations with non-adjacent form (NAF) method #62

Merged
merged 2 commits into from
Oct 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 38 additions & 43 deletions native/src/seal/evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "seal/util/uintarith.h"
#include "seal/util/polycore.h"
#include "seal/util/polyarithsmallmod.h"
#include "seal/util/numth.h"

using namespace std;
using namespace seal::util;
Expand Down Expand Up @@ -2425,6 +2426,12 @@ namespace seal
throw logic_error("invalid parameters");
}

// Check if Galois key is generated or not.
if (!galois_keys.has_key(galois_elt))
{
throw invalid_argument("Galois key not present");
}

uint64_t m = mul_safe(static_cast<uint64_t>(coeff_count), uint64_t(2));
uint64_t subgroup_size = static_cast<uint64_t>(coeff_count >> 1);
int n_power_of_two = get_power_of_two(static_cast<uint64_t>(coeff_count));
Expand All @@ -2439,49 +2446,6 @@ namespace seal
throw invalid_argument("encrypted size must be 2");
}

// Check if Galois key is generated or not.
// If not, attempt a bit decomposition; maybe we have log(n) many keys
if (!galois_keys.has_key(galois_elt))
{
// galois_elt = 3^order1 * (-1)^order2
uint64_t order1 = Zmstar_to_generator_.at(galois_elt).first;
uint64_t order2 = Zmstar_to_generator_.at(galois_elt).second;

// We use either 3 or -3 as our generator, depending on which gives smaller HW
uint64_t two_power_of_gen = 3;

// Does order1 or n/2-order1 have smaller Hamming weight?
if (hamming_weight(subgroup_size - order1) < hamming_weight(order1))
{
order1 = subgroup_size - order1;
try_mod_inverse(3, m, two_power_of_gen);
}

while(order1)
{
if (order1 & 1)
{
if (!galois_keys.has_key(two_power_of_gen))
{
throw invalid_argument("Galois key not present");
}
apply_galois_inplace(encrypted, two_power_of_gen, galois_keys, pool);
}
two_power_of_gen = mul_safe(two_power_of_gen, two_power_of_gen);
two_power_of_gen &= (m - 1);
order1 >>= 1;
}
if (order2)
{
if (!galois_keys.has_key(m - 1))
{
throw invalid_argument("Galois key not present");
}
apply_galois_inplace(encrypted, m - 1, galois_keys, pool);
}
return;
}

auto temp(allocate_poly(coeff_count, coeff_mod_count, pool));

// DO NOT CHANGE EXECUTION ORDER OF FOLLOWING SECTION
Expand Down Expand Up @@ -2584,12 +2548,43 @@ namespace seal
}

size_t coeff_count = context_data_ptr->parms().poly_modulus_degree();
int64_t subgroup_size = static_cast<uint64_t>(coeff_count >> 1);

// Check if Galois key is generated or not.
if (galois_keys.has_key(steps_to_galois_elt(steps, coeff_count)))
{
// Perform rotation and key switching
apply_galois_inplace(encrypted,
steps_to_galois_elt(steps, coeff_count),
galois_keys, move(pool));
}
else
{
// Convert the steps to NAF: guarantees using smallest HW
vector<int> naf_steps = naf(steps);

// If naf_steps contains only one element, then this is a power-of-two
// rotation and we would have expected not to get to this part of the
// if-statement.
if (naf_steps.size() == 1)
{
throw invalid_argument("Galois key not present");
}

for (size_t i = 0; i < naf_steps.size(); i++)
{
// We might have a NAF-term of size coeff_count / 2; this corresponds
// to no rotation so we skip it.
if (safe_cast<size_t>(abs(naf_steps[i])) == (coeff_count >> 1))
{
continue;
}

// Apply rotation for this step
rotate_internal(encrypted, naf_steps[i], galois_keys, pool);
}
}
}

void Evaluator::switch_key_inplace(
Ciphertext &encrypted,
Expand Down
1 change: 0 additions & 1 deletion native/src/seal/evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,6 @@ namespace seal
Dynamic memory allocations in the process are allocated from the memory
pool pointed to by the given MemoryPoolHandle.


The desired Galois automorphism is given as a Galois element, and must be
an odd integer in the interval [1, M-1], where M = 2*N, and N = poly_modulus_degree.
Used with batching, a Galois element 3^i % M corresponds to a cyclic row
Expand Down
24 changes: 24 additions & 0 deletions native/src/seal/util/numth.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,37 @@
#include "seal/util/common.h"
#include <stdexcept>
#include <cstdint>
#include <cmath>
#include <vector>
#include <tuple>
#include <algorithm>

namespace seal
{
namespace util
{
inline std::vector<int> naf(int value)
{
std::vector<int> res;

// Record the sign of the original value and compute abs
bool sign = value < 0;
value = std::abs(value);

// Transform to non-adjacent form (NAF)
for (int i = 0; value; i++)
{
int zi = (value % 2) ? 2 - (value % 4) : 0;
value = (value - zi) / 2;
if (zi)
{
res.push_back((sign ? -zi : zi) * (1 << i));
}
}

return res;
}

SEAL_NODISCARD inline std::uint64_t gcd(
std::uint64_t x, std::uint64_t y)
{
Expand Down
42 changes: 41 additions & 1 deletion native/tests/seal/util/numth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "gtest/gtest.h"
#include "seal/util/numth.h"
#include <cstdint>
#include <numeric>

using namespace seal::util;
using namespace std;
Expand Down Expand Up @@ -107,5 +108,44 @@ namespace SEALTest
ASSERT_TRUE(is_prime(36893488147419103ULL));
ASSERT_FALSE(is_prime(36893488147419107ULL));
}

TEST(NumberTheoryTest, NAF)
{
auto naf_vec = naf(0);
ASSERT_EQ(0, naf_vec.size());

naf_vec = naf(1);
ASSERT_EQ(1, naf_vec.size());
ASSERT_EQ(1, accumulate(naf_vec.begin(), naf_vec.end(), 0));
naf_vec = naf(-1);
ASSERT_EQ(1, naf_vec.size());
ASSERT_EQ(-1, accumulate(naf_vec.begin(), naf_vec.end(), 0));

naf_vec = naf(2);
ASSERT_EQ(1, naf_vec.size());
ASSERT_EQ(2, accumulate(naf_vec.begin(), naf_vec.end(), 0));
naf_vec = naf(-2);
ASSERT_EQ(1, naf_vec.size());
ASSERT_EQ(-2, accumulate(naf_vec.begin(), naf_vec.end(), 0));

naf_vec = naf(3);
ASSERT_EQ(2, naf_vec.size());
ASSERT_EQ(3, accumulate(naf_vec.begin(), naf_vec.end(), 0));
naf_vec = naf(-3);
ASSERT_EQ(2, naf_vec.size());
ASSERT_EQ(-3, accumulate(naf_vec.begin(), naf_vec.end(), 0));

naf_vec = naf(127);
ASSERT_EQ(2, naf_vec.size());
ASSERT_EQ(127, accumulate(naf_vec.begin(), naf_vec.end(), 0));
naf_vec = naf(-127);
ASSERT_EQ(2, naf_vec.size());
ASSERT_EQ(-127, accumulate(naf_vec.begin(), naf_vec.end(), 0));

naf_vec = naf(123);
ASSERT_EQ(123, accumulate(naf_vec.begin(), naf_vec.end(), 0));
naf_vec = naf(-123);
ASSERT_EQ(-123, accumulate(naf_vec.begin(), naf_vec.end(), 0));
}
}
}
}