-
Notifications
You must be signed in to change notification settings - Fork 7
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
feat: add crt
support
#10
base: main
Are you sure you want to change the base?
Conversation
This reverts commit b735ab4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments!
""" | ||
self.sigma = sigma | ||
|
||
# The lower and upper bounds of the range of the discrete Gaussian distribution are rounded to the nearest integer (check section 4 https://inferati.azureedge.net/docs/inferati-fhe-bfv.pdf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trivial, but indentation
|
||
def recover_with_centered_remainder(self): | ||
""" | ||
Recover the integer x from its CRT representation. The integer x is in (-q/2, q/2]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what situation would x
lie in the range of (-q/2, q/2]
, rather than [0, q)
? And can operations on x
in this range be implemented by applying the same operation to each CRT component xi
?
xi = self.xis[i] | ||
qi_star = self.q.q // self.q.qis[i] | ||
qi_tilde = pow( | ||
qi_star, -1, self.q.qis[i] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow python is so useful, didn't know there is a built-in function to get multiplicative inverse like this 👀
for j in range(len(rqi_polynomials)): | ||
coeff_crt_components.append(rqi_polynomials[j].coefficients[i]) | ||
coeff_crt_integer = CRTInteger(q, coeff_crt_components) | ||
coeff = coeff_crt_integer.recover_with_centered_remainder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is recover_with_centered_remainder()
rather than recover()
particularly used here?
class CRTPolynomial: | ||
def from_rq_polynomial_to_rqi_polynomials(rq_polynomial: Polynomial, q: Q): | ||
""" | ||
Reduce polynomial `a` coefficients to its CRT representations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is a
here? Did you maybe mean rq_polynomial
coefficients?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I got it after reading the test, but it might be better to not name the resulting/initial polynomial a
in the functions here
# Initialize quotient and remainder | ||
quotient = [0] * (len(dividend) - len(divisor) + 1) | ||
remainder = list(dividend) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a good practice to check if the leading coefficient of divisor
is non-zero.
@@ -69,5 +74,59 @@ def get_centered_remainder(x, modulus): | |||
return r if r <= modulus / 2 else r - modulus | |||
|
|||
|
|||
def poly_div(dividend: list[int], divisor: list[int]): | |||
dividend = [int(x) for x in dividend] | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's better to check that the degree of dividend
is greater than or equal to the degree of the divisor
first. otherwise initialization of the quotient
in line 81 won't be valid.
remainder[i + j] = rem | ||
|
||
# Remove leading zeroes in remainder, if any | ||
while remainder and remainder[0] == 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the entire remainder
is zero, then it will remove all elements of it and returning an empty list. we still want it to return remainder
filled with all zeros.
|
||
fx = [1] + [0] * (n-1) + [1] | ||
self.denominator = fx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you wanna rename denominator
=> divisor
to make it aligned with them in poly_div
?
return quotient, remainder | ||
|
||
|
||
def poly_mul(poly1: list[int], poly2: list[int]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a better practice to check poly1
and poly2
's leading coefficients are non-zero (assuming that 0th element = highest degree coefficient)
Crt
based decomposition of big modulusQ
to achieve RNS (https://eprint.iacr.org/2018/117)RLWE
class toBFV
(and corresponding files)PolynomialRing
classpoly_div
,poly_mul
andpoly_add
operations as performing them withnumpy
was losing precision when dealing with large integers