Skip to content

Commit

Permalink
Added BGV scheme and upgraded to SEAL4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
devharsh committed Nov 15, 2022
1 parent bc0034e commit e9f8cad
Show file tree
Hide file tree
Showing 13 changed files with 452 additions and 52 deletions.
8 changes: 8 additions & 0 deletions Pyfhel/Afhel/Afhel.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,21 @@ enum class scheme_t : std::uint8_t{
bfv = 0x1,
// Cheon-Kim-Kim-Song scheme
ckks = 0x2
// Brakerski-Gentry-Vaikunthanathan scheme
bgv = 0x3
};

static std::map<scheme_t, std::string> scheme_t_str {
{scheme_t::none, "none"},
{scheme_t::bfv, "bfv"},
{scheme_t::ckks, "ckks"},
{scheme_t::bgv, "bgv"},
};
static std::map<std::string, scheme_t> scheme_t_map {
{"none", scheme_t::none},
{"bfv", scheme_t::bfv},
{"ckks", scheme_t::ckks},
{"bgv", scheme_t::bgv},
};

//------------------------------- FHE backend ----------------------------------
Expand Down Expand Up @@ -99,13 +103,17 @@ class Afhel {
// ckks
virtual void encode_f(std::vector<double> &values, double scale, AfPtxt &plainVOut) = 0;
virtual void encode_c(std::vector<std::complex<double>> &values, double scale, AfPtxt &plainVOut) = 0;
// bgv
virtual void encode_g(std::vector<int64_t> &values, AfPtxt &plainOut) = 0;

// DECODE
// bfv
virtual void decode_i(AfPtxt &plain1, std::vector<int64_t> &valueVOut) = 0;
// ckks
virtual void decode_f(AfPtxt &plain1, std::vector<double> &valueVOut) = 0;
virtual void decode_c(AfPtxt &plain1, std::vector<std::complex<double>> &valueVOut) = 0;
// bgv
virtual void decode_g(AfPtxt &plain1, std::vector<int64_t> &valueVOut) = 0;

// -------------------------- RELINEARIZATION -------------------------
virtual void relinearize(AfCtxt &cipher1) = 0;
Expand Down
11 changes: 9 additions & 2 deletions Pyfhel/Afhel/Afhel.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ from libcpp.memory cimport shared_ptr, dynamic_pointer_cast
from libcpp.map cimport map as cpp_map
from libcpp.complex cimport complex as c_complex
from libcpp cimport bool
from numpy cimport int64_t, uint64_t, uint8_t

# Import our own wrapper for iostream classes, used for I/O ops
from Pyfhel.utils.iostream cimport istream, ostream, ifstream, ofstream

ctypedef long long int64_t
ctypedef unsigned long long uint64_t
ctypedef unsigned char uint8_t
ctypedef c_complex[double] cy_complex

#===============================================================================
Expand Down Expand Up @@ -49,6 +51,7 @@ cdef extern from "Afhel.h" nogil:
none,
bfv
ckks
bgv
cdef cpp_map scheme_t_str[scheme_t, string]

# FHE backend
Expand Down Expand Up @@ -107,13 +110,17 @@ cdef extern from "Afhel.h" nogil:
# ckks
void encode_f(vector[double] &values, double scale, AfPtxt &plainVOut) except +
void encode_c(vector[cy_complex] &values, double scale, AfPtxt &plainVOut) except +

# bgv
void encode_g(vector[int64_t] &values, AfPtxt &plainOut) except +

# DECODE
# bfv
void decode_i(AfPtxt &ptxt, vector[int64_t] &valueVOut) except +
# ckks
void decode_f(AfPtxt &ptxt, vector[double] &valueVOut) except +
void decode_c(AfPtxt &ptxt, vector[cy_complex] &valueVOut) except +
# bgv
void decode_g(AfPtxt &ptxt, vector[int64_t] &valueVOut) except +

# AUXILIARY
void data(AfPtxt &ptxt, uint64_t *dest) except +
Expand Down
62 changes: 47 additions & 15 deletions Pyfhel/Afhel/Afseal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Afseal::Afseal(const Afseal &otherAfseal)

this->bfvEncoder = make_shared<BatchEncoder>(*context);
this->ckksEncoder = make_shared<CKKSEncoder>(*context);
this->bgvEncoder = make_shared<BatchEncoder>(*context);
};

Afseal::~Afseal(){};
Expand All @@ -79,9 +80,9 @@ string Afseal::ContextGen(scheme_t scheme,
std::vector<int> qi_sizes,
std::vector<uint64_t> qi_values)
{
if (scheme != scheme_t::bfv && scheme != scheme_t::ckks)
if (scheme != scheme_t::bfv && scheme != scheme_t::bgv && scheme != scheme_t::ckks)
{
throw invalid_argument("<Afseal>: scheme must be bfv or ckks");
throw invalid_argument("<Afseal>: scheme must be bfv, bgv or ckks");
}
EncryptionParameters parms(scheme_map_to_seal[scheme]);
// Setting n
Expand Down Expand Up @@ -138,7 +139,11 @@ string Afseal::ContextGen(scheme_t scheme,
if (scheme == scheme_t::bfv)
{
this->bfvEncoder = make_shared<BatchEncoder>(*context);
}
}
else if (scheme == scheme_t::bgv)
{
this->bgvEncoder = make_shared<BatchEncoder>(*context);
}
else // ckks
{
this->ckksEncoder = make_shared<CKKSEncoder>(*context);
Expand Down Expand Up @@ -263,6 +268,16 @@ void Afseal::encode_c(std::vector<complex<double>> &values, double scale, AfPtxt
}
ckks_encoder->encode(values, scale, _dyn_p(ptxtOut));
}
// bgv
void Afseal::encode_g(vector<int64_t> &values, AfPtxt &ptxtOut)
{
auto bgv_encoder = this->get_bgv_encoder();
if (values.size() > bgv_encoder->slot_count())
{
throw range_error("<Afseal>: Data vector size is bigger than bgv nSlots");
}
bgvEncoder->encode(values, _dyn_p(ptxtOut));
}

// DECODE
// bfv
Expand All @@ -279,6 +294,11 @@ void Afseal::decode_c(AfPtxt &plain1, vector<std::complex<double>> &valueVOut)
{
this->get_ckks_encoder()->decode(_dyn_p(plain1), valueVOut);
}
// bgv
void Afseal::decode_g(AfPtxt &plain1, std::vector<int64_t> &valueVOut)
{
this->get_bgv_encoder()->decode(_dyn_p(plain1), valueVOut);
}

// AUXILIARY
void Afseal::data(AfPtxt &ptxt, uint64_t *dest)
Expand Down Expand Up @@ -408,7 +428,7 @@ void Afseal::multiply_plain_v(vector<AfCtxt *> &ctxtVInOut, vector<AfPtxt *> &pt
// ROTATION
void Afseal::rotate(AfCtxt &ctxt, int k)
{
if (this->get_scheme() == scheme_t::bfv)
if (this->get_scheme() == scheme_t::bfv || this->get_scheme() == scheme_t::bgv)
{
this->get_evaluator()->rotate_rows_inplace(_dyn_c(ctxt), k, *(this->get_rotateKeys()));
}
Expand All @@ -425,7 +445,7 @@ void Afseal::rotate_v(vector<AfCtxt *> &ctxtV, int k)
{
auto ev = this->get_evaluator();
auto &rtk = *(this->get_rotateKeys());
if (this->get_scheme() == scheme_t::bfv)
if (this->get_scheme() == scheme_t::bfv || this->get_scheme() == scheme_t::bgv)
{
vectorize(ctxtV,
[ev, k, rtk](AfCtxt c)
Expand Down Expand Up @@ -551,11 +571,19 @@ size_t Afseal::load_context(istream &in_stream, int sec)
if (parms.scheme() == scheme_type::bfv)
{
this->bfvEncoder = make_shared<BatchEncoder>(*context);
}
else if (parms.scheme() == scheme_type::bgv)
{
this->bgvEncoder = make_shared<BatchEncoder>(*context);
}
else if (parms.scheme() == scheme_type::ckks)
{
this->ckksEncoder = make_shared<CKKSEncoder>(*context);
}
else
{
throw std::logic_error("<Afseal>: Loaded context has invalid scheme");
}
this->evaluator = make_shared<Evaluator>(*context);
this->keyGenObj = make_shared<KeyGenerator>(*context);
return loaded_bytes;
Expand Down Expand Up @@ -708,6 +736,14 @@ shared_ptr<CKKSEncoder> inline Afseal::get_ckks_encoder()
}
return (this->ckksEncoder);
}
shared_ptr<BatchEncoder> inline Afseal::get_bgv_encoder()
{
if (this->encryptor == NULL)
{
throw std::logic_error("<Afseal>: BGV context not initialized");
}
return (this->bgvEncoder);
}
shared_ptr<SecretKey> inline Afseal::get_secretKey()
{
if (this->secretKey == NULL)
Expand Down Expand Up @@ -745,19 +781,15 @@ size_t Afseal::get_nSlots()
scheme_t scheme = this->get_scheme();
if (scheme == scheme_t::bfv)
{
if (this->bfvEncoder == NULL)
{
throw std::logic_error("<Afseal>: Context not initialized with BFV scheme");
}
return this->bfvEncoder->slot_count();
return this->get_bfv_encoder()->slot_count();
}
else if (scheme == scheme_t::ckks)
{
if (this->ckksEncoder == NULL)
{
throw std::logic_error("<Afseal>: Context not initialized with CKKS scheme");
}
return this->ckksEncoder->slot_count();
return this->get_ckks_encoder()->slot_count();
}
else if (scheme == scheme_t::bgv)
{
return this->get_bgv_encoder()->slot_count();
}
return -1;
}
Expand Down
12 changes: 10 additions & 2 deletions Pyfhel/Afhel/Afseal.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,13 @@ static std::map<seal::scheme_type, scheme_t> scheme_map_to_afhel {
{seal::scheme_type::none, scheme_t::none},
{seal::scheme_type::bfv, scheme_t::bfv},
{seal::scheme_type::ckks, scheme_t::ckks},
{seal::scheme_type::bgv, scheme_t::bgv},
};
static std::map<scheme_t, seal::scheme_type> scheme_map_to_seal {
{scheme_t::none, seal::scheme_type::none},
{scheme_t::bfv, seal::scheme_type::bfv},
{scheme_t::ckks, seal::scheme_type::ckks},
{scheme_t::bgv, seal::scheme_type::bgv},
};
static std::map<int, sec_level_type> sec_map{
{0, seal::sec_level_type::none},
Expand Down Expand Up @@ -238,8 +240,9 @@ class Afseal: public Afhel {
// --------------------------- ATTRIBUTES -----------------------------

std::shared_ptr<seal::SEALContext> context = NULL; /**< Context. Used for init*/
std::shared_ptr<seal::BatchEncoder> bfvEncoder = NULL; /**< Rotation in Batching. */
std::shared_ptr<seal::CKKSEncoder> ckksEncoder = NULL; /**< Rotation in Batching. */
std::shared_ptr<seal::BatchEncoder> bfvEncoder = NULL;
std::shared_ptr<seal::CKKSEncoder> ckksEncoder = NULL;
std::shared_ptr<seal::BatchEncoder> bgvEncoder = NULL;

std::shared_ptr<seal::KeyGenerator> keyGenObj = NULL; /**< Key Generator Object.*/
std::shared_ptr<seal::SecretKey> secretKey = NULL; /**< Secret key.*/
Expand Down Expand Up @@ -296,13 +299,17 @@ class Afseal: public Afhel {
// ckks
void encode_f(vector<double> &values, double scale, AfPtxt &ptxtVOut);
void encode_c(vector<std::complex<double>> &values, double scale, AfPtxt &ptxtVOut);
// bgv
void encode_g(vector<int64_t> &values, AfPtxt &plainOut);

// DECODE
// bfv
void decode_i(AfPtxt &ptxt, vector<int64_t> &valueVOut);
// ckks
void decode_f(AfPtxt &ptxt, vector<double> &valueVOut);
void decode_c(AfPtxt &ptxt, vector<std::complex<double>> &valueVOut);
// bgv
void decode_g(AfPtxt &ptxt, vector<int64_t> &valueVOut);

// AUXILIARY
void data(AfPtxt &ptxt, uint64_t *dest);
Expand Down Expand Up @@ -430,6 +437,7 @@ class Afseal: public Afhel {
inline shared_ptr<Decryptor> get_decryptor();
inline shared_ptr<BatchEncoder> get_bfv_encoder();
inline shared_ptr<CKKSEncoder> get_ckks_encoder();
inline shared_ptr<BatchEncoder> get_bgv_encoder();
inline shared_ptr<SecretKey> get_secretKey();
inline shared_ptr<PublicKey> get_publicKey();
inline shared_ptr<RelinKeys> get_relinKeys();
Expand Down
15 changes: 8 additions & 7 deletions Pyfhel/PyCtxt.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ cdef class PyCtxt:
@property
def scheme(self):
"""scheme: returns the scheme type.
Can be set to: none, bfv (INTEGER) or ckks (FRACTIONAL).
Can be set to: none, bfv, bgv (INTEGER) or ckks (FRACTIONAL).
See Also:
:func:`~Pyfhel.utils.to_Scheme_t`
Expand Down Expand Up @@ -251,7 +251,8 @@ cdef class PyCtxt:
* ('int', 'INTEGER', int, 1, Scheme_t.bfv) -> integer scheme.
* ('float', 'FRACTIONAL', float, 2, Scheme_t.ckks) -> fractional scheme.
* (3, Scheme_t.bgv) -> integer scheme.
Return:
size_t: number of loaded bytes.
Expand Down Expand Up @@ -545,7 +546,7 @@ cdef class PyCtxt:
"""
if not isinstance(divisor, (int, float, list, np.ndarray, PyPtxt)):
raise TypeError("<Pyfhel ERROR> divisor must be float, int, array|list "
"or PyPtxt with bfv/ckks scheme (is %s instead)"
"or PyPtxt with bfv/bgv/ckks scheme (is %s instead)"
%(type(divisor)))
if isinstance(divisor, PyPtxt): # If ptxt, decode to get inverse
divisor = divisor.decode()
Expand All @@ -571,7 +572,7 @@ cdef class PyCtxt:
"""
if not isinstance(divisor, (int, float, list, np.ndarray, PyPtxt)):
raise TypeError("<Pyfhel ERROR> divisor must be float, int, array|list "
"or PyPtxt with bfv/ckks scheme (is %s instead)"
"or PyPtxt with bfv/bgv/ckks scheme (is %s instead)"
%(type(divisor)))
if isinstance(divisor, PyPtxt): # If ptxt, decode to get inverse
divisor = divisor.decode()
Expand Down Expand Up @@ -680,7 +681,7 @@ cdef class PyCtxt:
"""__repr__()
Prints information about the current ciphertext"""
if self.scheme==Scheme_t.bfv:
if self.scheme==Scheme_t.bfv or self.scheme==Scheme_t.bgv:
scheme_dep_info = 'noiseBudget=' + str(self.noiseBudget) \
if self.noiseBudget!=-1 else "?"
elif self.scheme==Scheme_t.ckks:
Expand Down Expand Up @@ -781,7 +782,7 @@ cdef class PyCtxt:
if other.ndim == 0: # If scalar, replicate
other = other.reshape([1])
# Compute inverse. Int: https://stackoverflow.com/questions/4798654
if self.scheme == Scheme_t.bfv:
if self.scheme == Scheme_t.bfv or self.scheme == Scheme_t.bgv:
other = other.astype(np.int64)
t = self._pyfhel.t
inverse = np.array([pow(int(other[i]), t-2, t)for i in range(other.shape[0])])
Expand Down
2 changes: 1 addition & 1 deletion Pyfhel/PyPoly.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ cdef class PyPoly:
def _scheme(self):
"""_scheme: returns the scheme type.
Can be set to: 0-None, 1-BFV, 2-CKKS
Can be set to: 0-None, 1-BFV, 2-CKKS, 3-BGV.
See Also:
:func:`~Pyfhel.utils.to_scheme_t`
Expand Down
7 changes: 4 additions & 3 deletions Pyfhel/PyPtxt.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ cdef class PyPtxt:
def scheme(self):
"""scheme: returns the scheme type.
Can be set to: 0-none, 1-bfv, 2-ckks
Can be set to: 0-none, 1-bfv, 2-ckks, 3-bgv
See Also:
:func:`~Pyfhel.utils.to_Scheme_t`
Expand Down Expand Up @@ -229,6 +229,7 @@ cdef class PyPtxt:
scheme: (:obj: `str`) String or type describing the scheme:
* ('int', 'integer', int, 1, scheme_t.bfv) -> integer scheme.
* ('float', 'double', float, 2, scheme_t.ckks) -> fractional scheme.
* (3, scheme_t.bgv) -> integer scheme.
"""
if self._pyfhel is None:
raise ValueError("<Pyfhel ERROR> plaintext loading requires a Pyfhel instance")
Expand All @@ -246,8 +247,8 @@ cdef class PyPtxt:

def __int__(self):
"""returns the integer in the first slot of the plaintext"""
if (self._scheme != scheme_t.bfv):
raise RuntimeError("<Pyfhel ERROR> wrong PyPtxt scheme for automatic encoding (not bfv)")
if (self._scheme != scheme_t.bfv and self._scheme != scheme_t.bgv):
raise RuntimeError("<Pyfhel ERROR> wrong PyPtxt scheme for automatic encoding (not bfv/bfv)")
return int(np.array(self._pyfhel.decodeInt(self))[0])

def __float__(self):
Expand Down
Loading

0 comments on commit e9f8cad

Please sign in to comment.