SEAL中用CRT完成多项式编码相关(SEAL/Polycrt.h 2.3.0)


终于还是要读源码了,终究摆脱不了数学的噩梦,先从读懂他们的代码开始。
文件原位置SEAL/Polycrt.h

介绍

提供CRT批处理功能。 如果多项式模数是 xN+1x^N+1,并且明文模数是素数 TT 使得 T1mod  2NT \equiv 1 \mod 2N ,则PolyCRTBuilder允许SEAL明文元素被视为2×(N/2)2 \times (N/2) 的矩阵,在这种加密矩阵上执行的同态操作是应用系数(槽),为可矢量化的计算启用强大的SIMD功能。 此功能通常在同态加密文献中称为“批处理”。

  • par数学背景
    从数学上讲,如果poly_modulus是 xN+1x^N+1NN2​​2 的幂,plain_modulus是素数 TT,使得 2N2N 整除 T1T-1,则模TT的整数包含 一个 2N2N 的原始根,多项式XN+1X ^ N + 1分裂成nn个不同的线性因子,如XN+1=Xa1XaNmod  TX ^ N + 1 =(X-a_1)*\dots *(X-a_N)\mod T,其中常数a1ana_1,\dots ,a_n 都是不同的原始的第2N2N个整数的根在整数模TT中。中国剩余定理(CRT)表明在这种情况下明文空间ZT[X]/(XN+1)Z_T [X] /(X ^ N + 1)NN同构(作为代数)折叠领域的直接产品ZTZ_T。同构很容易在两个方向上明确计算,这就是这个类的作用。此外,扩张的伽罗瓦群是(Z/2NZ)=Z/2Z×Z/(N/2)(Z / 2NZ)*〜= Z / 2Z×Z /(N / 2),其对原始根的作用很容易描述。由于批处理时隙与统一的原始根对应1对1,因此通过置换时隙在明文上应用Galois自同构。通过应用伽罗瓦群的两个循环子群的生成器,我们可以有效地将明文视为2乘N/2N / 2 矩阵,并且能够实现循环行旋转和列旋转(行交换)。

  • par有效参数
    是否可以使用批处理取决于是否适当选择了明文模数。 因此,要构造PolyCRTBuilder,用户必须提供SEALContext的实例,以使其关联的EncryptionParameterQualifiers对象将flags_set和enable_batching设置为true。

  • par重载
    对于分解函数,我们提供了两个有关操作期间所需分配中使用的内存池的重载。 在一次重载中,PolyCRTBuilder的本地内存池(用于存储预计算结果和其他成员变量)用于此目的,而在另一个重载中,用户可以提供要使用的MemoryPoolHandle。 这是为了允许多个线程同时使用一个PolyCRTBuilder,而不会在操作期间发生的分配中遇到线程争用。 例如,可以跨任意数量的线程共享一个PolyCRTBuilder,但是在每个线程中通过为其提供一个线程本地MemoryPoolHandle来调用加密函数。 对于开发人员而言,了解其工作原理以避免不必要的性能瓶颈非常重要。

    @有关加密参数的详细信息,请参阅EncryptionParameters。
    @see EncryptionParameterQualifiers了解有关参数限定符的更多信息。
    @see Evaluator用于旋转加密矩阵的行和列。

overview

polycrtbuilder

构造函数

基本构造

PolyCRTBuilder(const SEALContext &context, const MemoryPoolHandle &pool = MemoryPoolHandle::Global());

创建PolyCRTBuilder。 通过SEALContext对象提供的加密参数必须支持批处理。 动态分配的成员变量是从给定的MemoryPoolHandle指向的内存池中分配的。 默认情况下,使用全局内存池。

     @param [in] context SEALContext
     @param [in] pool MemoryPoolHandle 指向有效的内存池
     如果加密参数对批处理无效,则@throws std :: invalid_argument
     如果池未初始化,则@throws std :: invalid_argument
    PolyCRTBuilder::PolyCRTBuilder(const SEALContext &context, const MemoryPoolHandle &pool) :
        pool_(pool), parms_(context.parms()),
        ntt_tables_(pool_),
        slots_(parms_.poly_modulus().coeff_count() - 1),
        qualifiers_(context.qualifiers())
    {
        int coeff_count = parms_.poly_modulus().coeff_count();

        // Verify parameters
        if (!qualifiers_.parameters_set)
        {
            throw invalid_argument("encryption parameters are not set correctly");
        }
        if (!qualifiers_.enable_batching)
        {
            throw invalid_argument("encryption parameters are not valid for batching");
        }
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        // Set mod_ and polymod_
        mod_ = parms_.plain_modulus();
        polymod_ = PolyModulus(parms_.poly_modulus().pointer(), coeff_count, 
            parms_.poly_modulus().coeff_uint64_count());

        // Reserve space for all of the primitive roots
        roots_of_unity_ = allocate_uint(slots_, pool_);

        // Copy over NTT tables (switching to local pool)
        ntt_tables_ = context.plain_ntt_tables_;

        // Fill the vector of roots of unity with all distinct odd powers of generator.
        // These are all the primitive (2*slots_)-th roots of unity in integers modulo parms_.plain_modulus().
        populate_roots_of_unity_vector();

        // Populate matrix representation index map
        populate_matrix_reps_index_map();
    }

深拷贝

PolyCRTBuilder(const PolyCRTBuilder &copy);

创建给定PolyCRTBuilder的深拷贝。

     @param [in] copy 要复制的PolyCRTBuilder
PolyCRTBuilder::PolyCRTBuilder(const PolyCRTBuilder &copy) :
        pool_(copy.pool_), parms_(copy.parms_),
        ntt_tables_(copy.ntt_tables_),
        slots_(copy.slots_),
        qualifiers_(copy.qualifiers_)
    {
        int coeff_uint64_count = parms_.plain_modulus().uint64_count();

        // Allocate and copy over roots of unity
        roots_of_unity_ = allocate_poly(slots_, coeff_uint64_count, pool_);
        set_poly_poly(copy.roots_of_unity_.get(), slots_, coeff_uint64_count, roots_of_unity_.get());

        // Set mod_ and polymod_
        mod_ = parms_.plain_modulus();
        polymod_ = PolyModulus(parms_.poly_modulus().pointer(), parms_.poly_modulus().coeff_count(), 
            parms_.poly_modulus().coeff_uint64_count());
    }

移动

PolyCRTBuilder(PolyCRTBuilder &&source) = default;

通过移动给定的一个来创建一个新的PolyCRTBuilder。

     @param [in] source 要移动的PolyCRTBuilder

编码(compose)

uint64_t

void compose(const std::vectorstd::uint64_t &values, Plaintext &destination);

从给定矩阵创建SEAL明文。 该函数将以明文模数为模的给定整数矩阵“批处理”为SEAL明文元素,并将结果存储在目标参数中。 输入向量的大小必须至多等于多项式模数的大小。 元素的前半部分代表矩阵的第一行,后半部分代表第二行。 矩阵中的大小最大等于明文模数,以表示有效的SEAL明文。

     @param [in] values 整数矩阵模数为明文模数
     @param [out] destination 用结果覆盖的明文多项式
     @throws std :: invalid_argument 如果值太大,抛出异常
void PolyCRTBuilder::compose(const vector<int64_t> &values_matrix, Plaintext &destination)
    {
        // Validate input parameters,验证输入的参数,如果矩阵的大小大于crtbuilder 的slot_counts,则报错。
        if (values_matrix.size() > slots_)
        {
            throw logic_error("values_matrix size is too large");
        }
#ifdef SEAL_DEBUG
        for (auto v : values_matrix)
        {
            // Validate the i-th input
            if (v >= mod_.value())
            {
                throw invalid_argument("input value is larger than plain_modulus");
            }
        }
#endif
        // 当前输入矩阵的大小
        int input_matrix_size = values_matrix.size();

        // Set destination to full size,将明文多项式的目标位置预先设置为slot_counts        
        destination.resize(slots_);

        // First write the values to destination coefficients. Read in top row, then bottom row. 不全的数组数据用0补充。
        for (int i = 0; i < input_matrix_size; i++)
        {
            *(destination.pointer() + matrix_reps_index_map_[i]) = values_matrix[i];
        }        
        for (int i = input_matrix_size; i < slots_; i++)
        {
            *(destination.pointer() + matrix_reps_index_map_[i]) = 0;
        }

        // Transform destination using inverse of negacyclic NTT
        // Note: We already performed bit-reversal when reading in the matrix
        inverse_ntt_negacyclic_harvey(destination.pointer(), ntt_tables_);

int64_t

void compose(const std::vectorstd::int64_t &values, Plaintext &destination);

同上,只类型不同。
从给定矩阵创建SEAL明文。 该函数将以明文模数为模的给定整数矩阵“批处理”为SEAL明文元素,并将结果存储在目标参数中。 输入向量的大小必须至多等于多项式模数的大小。 元素的前半部分代表矩阵的第一行,后半部分代表第二行。 矩阵中的数字最多等于明文模数,以表示有效的SEAL明文。

     @param [in] values 整数矩阵模数为明文模数
     @param [out] destination 用结果覆盖的明文多项式
     @throws std :: invalid_argument 如果值太大
void PolyCRTBuilder::compose(const vector<int64_t> &values_matrix, Plaintext &destination)
    {
        // Validate input parameters
        if (values_matrix.size() > slots_)
        {
            throw logic_error("values_matrix size is too large");
        }

        uint64_t plain_modulus_div_two = mod_.value() >> 1;
#ifdef SEAL_DEBUG
        for (auto v : values_matrix)
        {
            // Validate the i-th input
            if (abs(v) > plain_modulus_div_two)
            {
                throw invalid_argument("input value is larger than plain_modulus");
            }
        }
#endif
        int input_matrix_size = values_matrix.size();

        // Set destination to full size
        destination.resize(slots_);

        // First write the values to destination coefficients. Read 
        // in top row, then bottom row.
        for (int i = 0; i < input_matrix_size; i++)
        {
            *(destination.pointer() + matrix_reps_index_map_[i]) = (values_matrix[i] < 0) ? 
                (mod_.value() + values_matrix[i]) : values_matrix[i];
        }
        for (int i = input_matrix_size; i < slots_; i++)
        {
            *(destination.pointer() + matrix_reps_index_map_[i]) = 0;
        }

        // Transform destination using inverse of negacyclic NTT
        // Note: We already performed bit-reversal when reading in the matrix
        inverse_ntt_negacyclic_harvey(destination.pointer(), ntt_tables_);
    }

plaintext

void PolyCRTBuilder::compose(Plaintext &plain, const MemoryPoolHandle &pool)

从给定矩阵创建SEAL明文。该函数将给定的整数模量的整数给定矩阵“批处理”为准备加密的SEAL明文。矩阵作为明文元素给出,其前 N/2N / 2 系数表示矩阵的第一行,后N/2N / 2系数表示第二行,其中 NN 表示多项式模数的程度。输入明文必须具有小于多项式模数的递减,并且系数小于明文模数,即它必须是加密参数的有效明文。进程中的动态内存分配是从给定MemoryPoolHandle指向的内存池中分配的。

    @param [in] plain整数矩阵模数为明文模数
    @param [in] pool MemoryPoolHandle指向有效的内存池
    如果plain对加密参数无效,则@throws std :: invalid_argument
    如果池未初始化,则@throws std :: invalid_argument
    * /
 void PolyCRTBuilder::compose(Plaintext &plain, const MemoryPoolHandle &pool)
    {
        int coeff_count = parms_.poly_modulus().coeff_count();

        // Validate input parameters
        if (plain.coeff_count() > coeff_count || 
            (plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#ifdef SEAL_DEBUG
        if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
            plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#endif
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        // We need to permute the coefficients of plain. To do this, we allocate 
        // temporary space.
        int input_plain_coeff_count = min(plain.coeff_count(), slots_);
        Pointer temp(allocate_uint(input_plain_coeff_count, pool));
        set_uint_uint(plain.pointer(), input_plain_coeff_count, temp.get());

        // Set plain to full slot count size.
        plain.resize(slots_);

        // First write the values to destination coefficients. Read 
        // in top row, then bottom row.
        for (int i = 0; i < input_plain_coeff_count; i++)
        {
            *(plain.pointer() + matrix_reps_index_map_[i]) = temp[i];
        }
        for (int i = input_plain_coeff_count; i < slots_; i++)
        {
            *(plain.pointer() + matrix_reps_index_map_[i]) = 0;
        }

        // Transform destination using inverse of negacyclic NTT
        // Note: We already performed bit-reversal when reading in the matrix
        inverse_ntt_negacyclic_harvey(plain.pointer(), ntt_tables_);
    }

解码(decompose)

uint64_t

void PolyCRTBuilder::decompose(const Plaintext &plain, vector<uint64_t> &destination, const MemoryPoolHandle &pool)

iverse of compose。 该函数将给定SEAL明文“解”为以明文模数为模的整数矩阵,并将结果存储在目标参数中。 输入明文必须具有小于多项式模数的递减,并且系数小于明文模数,即它必须是加密参数的有效明文。 进程中的动态内存分配是从给定MemoryPoolHandle指向的内存池中分配的。

     @param [in] plain要解开的明文多项式
     @param [out] destination要用槽的值覆盖的向量
     @param [in] pool MemoryPoolHandle指向有效的内存池
     如果plain对加密参数无效,则@throws std :: invalid_argument
     如果池未初始化,则@throws std :: invalid_argument
void PolyCRTBuilder::decompose(const Plaintext &plain, vector<uint64_t> &destination, const MemoryPoolHandle &pool) 
    {
        int coeff_count = parms_.poly_modulus().coeff_count();

        // Validate input parameters
        if (plain.coeff_count() > coeff_count || 
            (plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#ifdef SEAL_DEBUG
        if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
            plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#endif
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        // Set destination size
        destination.resize(slots_);

        // Never include the leading zero coefficient (if present)
        int plain_coeff_count = min(plain.coeff_count(), slots_);

        Pointer temp_dest(allocate_uint(slots_, pool));

        // Make a copy of poly
        set_uint_uint(plain.pointer(), plain_coeff_count, temp_dest.get());
        set_zero_uint(slots_ - plain_coeff_count, temp_dest.get() + plain_coeff_count);

        // Transform destination using negacyclic NTT.
        ntt_negacyclic_harvey(temp_dest.get(), ntt_tables_);

        // Read top row
        for (int i = 0; i < slots_; i++)
        {
            destination[i] = temp_dest[matrix_reps_index_map_[i]];
        }
    }

int64_t

void PolyCRTBuilder::decompose(const Plaintext &plain, vector<int64_t> &destination, const MemoryPoolHandle &pool)

同上

void PolyCRTBuilder::decompose(const Plaintext &plain, vector<int64_t> &destination,
        const MemoryPoolHandle &pool)
    {
        int coeff_count = parms_.poly_modulus().coeff_count();

        // Validate input parameters
        if (plain.coeff_count() > coeff_count || 
            (plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#ifdef SEAL_DEBUG
        if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
            plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#endif
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        // Set destination size
        destination.resize(slots_);

        // Never include the leading zero coefficient (if present)
        int plain_coeff_count = min(plain.coeff_count(), slots_);

        Pointer temp_dest(allocate_uint(slots_, pool));

        // Make a copy of poly
        set_uint_uint(plain.pointer(), plain_coeff_count, temp_dest.get());
        set_zero_uint(slots_ - plain_coeff_count, temp_dest.get() + plain_coeff_count);

        // Transform destination using negacyclic NTT.
        ntt_negacyclic_harvey(temp_dest.get(), ntt_tables_);

        // Read top row, then bottom row
        uint64_t plain_modulus_div_two = mod_.value() >> 1;
        for (int i = 0; i < slots_; i++)
        {
            int64_t curr_value = temp_dest[matrix_reps_index_map_[i]];
            destination[i] = (curr_value > plain_modulus_div_two) ?
                (curr_value - mod_.value()) : curr_value;
        }
    }

plaintext

void PolyCRTBuilder::decompose(Plaintext &plain, const MemoryPoolHandle &pool)

void PolyCRTBuilder::decompose(Plaintext &plain, const MemoryPoolHandle &pool)
    {
        int coeff_count = parms_.poly_modulus().coeff_count();

        // Validate input parameters
        if (plain.coeff_count() > coeff_count || 
            (plain.coeff_count() == coeff_count && plain[coeff_count - 1] != 0))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#ifdef SEAL_DEBUG
        if (plain.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain.pointer(),
            plain.coeff_count(), 1, parms_.plain_modulus().pointer(), parms_.plain_modulus().uint64_count()))
        {
            throw invalid_argument("plain is not valid for encryption parameters");
        }
#endif
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        // Never include the leading zero coefficient (if present)
        int plain_coeff_count = min(plain.coeff_count(), slots_);

        // Allocate temporary space to store a wide copy of plain
        Pointer temp(allocate_uint(slots_, pool));

        // Make a copy of poly
        set_uint_uint(plain.pointer(), plain_coeff_count, temp.get());
        set_zero_uint(slots_ - plain_coeff_count, temp.get() + plain_coeff_count);

        // Transform destination using negacyclic NTT.
        ntt_negacyclic_harvey(temp.get(), ntt_tables_);

        // Set plain to full slot count size (note that all new coefficients are 
        // set to zero).
        plain.resize(slots_);

        // Read top row, then bottom row
        for (int i = 0; i < slots_; i++)
        {
            *(plain.pointer() + i) = temp[matrix_reps_index_map_[i]];
        }
    }

其他

void PolyCRTBuilder::populate_roots_of_unity_vector()

    void PolyCRTBuilder::populate_roots_of_unity_vector()
    {
        uint64_t generator_sq = multiply_uint_uint_mod(ntt_tables_.get_root(), ntt_tables_.get_root(), mod_);
        roots_of_unity_[0] = ntt_tables_.get_root();

        for (int i = 0; i < slots_ - 1; i++)
        {
            roots_of_unity_[i + 1] = multiply_uint_uint_mod(roots_of_unity_[i], generator_sq, mod_);
        }
    }

void PolyCRTBuilder::populate_matrix_reps_index_map()

    void PolyCRTBuilder::populate_matrix_reps_index_map()
    {
        int logn = get_power_of_two(slots_);
        uint32_t row_size = slots_ >> 1;
        matrix_reps_index_map_.resize(slots_);

        // Copy from the matrix to the value vectors 
        uint32_t gen = 3;
        uint32_t pos = 1;
        uint32_t m = slots_ << 1;
        for (uint32_t i = 0; i < row_size; i++)
        {
            // Position in normal bit order
            uint32_t index1 = (pos - 1) >> 1;
            uint32_t index2 = (m - pos - 1) >> 1;

            // Set the bit-reversed locations
            matrix_reps_index_map_[i] = util::reverse_bits(index1, logn);
            matrix_reps_index_map_[row_size | i] = util::reverse_bits(index2, logn);

            // Next primitive root
            pos *= gen;
            pos &= (m - 1);
        }
    }

inline int slot_count()

inline int slot_count() const
        {
            return slots_;
        }

inline void reverse_bits(std::uint64_t *input)

inline void reverse_bits(std::uint64_t *input)
        {
#ifdef SEAL_DEBUG
            if (input == nullptr)
            {
                throw std::invalid_argument("input cannot be null");
            }
#endif
            std::uint32_t n = parms_.poly_modulus().coeff_count() - 1;
            int logn = util::get_power_of_two(n);
            for (std::uint32_t i = 0; i < n; i++)
            {
                std::uint32_t reversed_i = util::reverse_bits(i, logn);
                if (i < reversed_i)
                {
                    std::swap(input[i], input[reversed_i]);
                }
            }
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章