From 7f4e619b15f7313a8ed821dd1e1d6c1ebfc65e26 Mon Sep 17 00:00:00 2001 From: Hwang In Tak Date: Fri, 6 Sep 2024 10:11:58 +0000 Subject: [PATCH] feat: Merge PolyEvaluator and FourierEvaluator --- math/poly/asm_vec_cmplx.go | 11 - math/poly/asm_vec_cmplx_amd64.go | 16 -- math/poly/asm_vec_cmplx_amd64.s | 24 -- math/poly/fourier_evaluator.go | 197 ---------------- math/poly/fourier_ops.go | 392 +++++-------------------------- math/poly/fourier_transform.go | 120 +++++----- math/poly/karatsuba.go | 170 -------------- math/poly/poly_evaluator.go | 242 ++++++++++++++++++- math/poly/poly_mul.go | 309 ++++++++++++++++++++++++ math/poly/poly_ops.go | 40 ++-- math/poly/poly_test.go | 77 ++++-- mktfhe/bootstrap_keygen.go | 2 +- mktfhe/fourier_glwe_conv.go | 36 +-- mktfhe/fourier_glwe_enc.go | 4 +- mktfhe/fourier_glwe_ops.go | 30 +-- mktfhe/glwe_enc.go | 4 +- mktfhe/glwe_ops.go | 28 +++ mktfhe/product.go | 96 ++++---- tfhe/bootstrap.go | 72 +++--- tfhe/encryptor.go | 13 +- tfhe/encryptor_public.go | 8 +- tfhe/evaluator.go | 16 +- tfhe/fourier_glwe_conv.go | 20 +- tfhe/fourier_glwe_enc.go | 6 +- tfhe/fourier_glwe_ops.go | 36 +-- tfhe/glwe_enc.go | 16 +- tfhe/glwe_enc_public.go | 6 +- tfhe/glwe_ops.go | 28 +++ tfhe/lwe_enc.go | 6 +- tfhe/lwe_enc_public.go | 2 +- tfhe/product.go | 18 +- 31 files changed, 981 insertions(+), 1064 deletions(-) delete mode 100644 math/poly/fourier_evaluator.go delete mode 100644 math/poly/karatsuba.go create mode 100644 math/poly/poly_mul.go diff --git a/math/poly/asm_vec_cmplx.go b/math/poly/asm_vec_cmplx.go index 0cdc1a8..4f2593b 100644 --- a/math/poly/asm_vec_cmplx.go +++ b/math/poly/asm_vec_cmplx.go @@ -2,17 +2,6 @@ package poly -import ( - "math" -) - -// roundCmplxAssign computes vOut = round(v0). -func roundCmplxAssign(v0, vOut []float64) { - for i := range vOut { - vOut[i] = math.Round(v0[i]) - } -} - // addCmplxAssign computes vOut = v0 + v1. func addCmplxAssign(v0, v1, vOut []float64) { for i := range vOut { diff --git a/math/poly/asm_vec_cmplx_amd64.go b/math/poly/asm_vec_cmplx_amd64.go index ae664fa..5c44c2f 100644 --- a/math/poly/asm_vec_cmplx_amd64.go +++ b/math/poly/asm_vec_cmplx_amd64.go @@ -3,25 +3,9 @@ package poly import ( - "math" - "golang.org/x/sys/cpu" ) -func roundCmplxAssignAVX2(v0, vOut []float64) - -// roundCmplxAssign computes vOut = round(v0). -func roundCmplxAssign(v0, vOut []float64) { - if cpu.X86.HasAVX2 { - roundCmplxAssignAVX2(v0, vOut) - return - } - - for i := range vOut { - vOut[i] = math.Round(v0[i]) - } -} - func addCmplxAssignAVX2(v0, v1, vOut []float64) // addCmplxAssign computes vOut = v0 + v1. diff --git a/math/poly/asm_vec_cmplx_amd64.s b/math/poly/asm_vec_cmplx_amd64.s index 81d666e..dd013ed 100644 --- a/math/poly/asm_vec_cmplx_amd64.s +++ b/math/poly/asm_vec_cmplx_amd64.s @@ -2,30 +2,6 @@ #include "textflag.h" -TEXT ·roundCmplxAssignAVX2(SB), NOSPLIT, $0-48 - MOVQ v0_base+0(FP), AX - MOVQ vOut_base+24(FP), CX - - MOVQ vOut_len+32(FP), DX - - XORQ SI, SI - JMP loop_end - -loop_body: - VMOVUPD (AX)(SI*8), Y0 - - VROUNDPD $0, Y0, Y0 - - VMOVUPD Y0, (CX)(SI*8) - - ADDQ $4, SI - -loop_end: - CMPQ SI, DX - JL loop_body - - RET - TEXT ·addCmplxAssignAVX2(SB), NOSPLIT, $0-72 MOVQ v0_base+0(FP), AX MOVQ v1_base+24(FP), BX diff --git a/math/poly/fourier_evaluator.go b/math/poly/fourier_evaluator.go deleted file mode 100644 index 0b910e5..0000000 --- a/math/poly/fourier_evaluator.go +++ /dev/null @@ -1,197 +0,0 @@ -package poly - -import ( - "math" - "math/cmplx" - - "github.com/sp301415/tfhe-go/math/num" - "github.com/sp301415/tfhe-go/math/vec" -) - -// FourierEvaluator computes polynomial algorithms over the fourier domain. -// -// Operations usually take two forms: for example, -// - Add(fp0, fp1) adds fp0, fp1, allocates a new polynomial to store the result and returns it. -// - AddAssign(fp0, fp1, fpOut) adds fp0, fp1 and writes the result to pre-allocated fpOut without returning. -// -// Note that in most cases, fp0, fp1, and fpOut can overlap. -// However, for operations that cannot, InPlace methods are implemented separately. -// -// For performance reasons, most methods in this package don't implement bound checks. -// If length mismatch happens, it may panic or produce wrong results. -type FourierEvaluator[T num.Integer] struct { - // degree is the degree of polynomial that this transformer can handle. - degree int - // q is a float64 value of Q. - q float64 - // qInv is a float64 value of 1/Q. - qInv float64 - - // tw holds the twiddle factors for fourier transform. - // This is stored as "long" form, so that access to the factors are contiguous. - // Unlike other complex128 slices, tw is in natural representation. - tw []complex128 - // twInv holds the twiddle factors for inverse fourier transform. - // This is stored as "long" form, so that access to the factors are contiguous. - // Unlike other complex128 slices, twInv is in natural representation. - twInv []complex128 - // twMono holds the twiddle factors for monomial fourier transform. - // Unlike other complex128 slices, twMono is in natural representation. - twMono []complex128 - // twMonoIdx holds the precomputed bit-reversed index for monomial fourier transform. - // Equivalent to BitReverse([-1, 3, 7, ..., 2N-1]). - twMonoIdx []int - - // buffer holds the buffer values for this FourierEvaluator. - buffer fourierBuffer[T] -} - -// fourierBuffer contains buffer values for FourierEvaluator. -type fourierBuffer[T num.Integer] struct { - // fp holds the FFT value of p. - fp FourierPoly - // fpInv holds the InvFFT value of fp. - fpInv FourierPoly - - // fpMul holds the FFT value of p in multiplication. - fpMul FourierPoly - - // pSplit holds the split value of p in [*FourierEvaluator.PolyMulBinary]. - pSplit Poly[T] - // fpSplit holds the fourier transformed pSplit. - fpSplit FourierPoly -} - -// NewFourierEvaluator allocates an empty FourierEvaluator with degree N. -// -// Panics when N is not a power of two, or when N is smaller than MinDegree or larger than MaxDegree. -func NewFourierEvaluator[T num.Integer](N int) *FourierEvaluator[T] { - switch { - case !num.IsPowerOfTwo(N): - panic("degree not power of two") - case N < MinDegree: - panic("degree smaller than MinDegree") - case N > MaxDegree: - panic("degree larger than MaxDegree") - } - - Q := math.Exp2(float64(num.SizeT[T]())) - QInv := math.Exp2(-float64(num.SizeT[T]())) - - tw, twInv := genTwiddleFactors(N / 2) - - twMono := make([]complex128, 2*N) - for j := 0; j < 2*N; j++ { - e := -math.Pi * float64(j) / float64(N) - twMono[j] = cmplx.Exp(complex(0, e)) - } - - twMonoIdx := make([]int, N/2) - twMonoIdx[0] = 2*N - 1 - for i := 1; i < N/2; i++ { - twMonoIdx[i] = 4*i - 1 - } - vec.BitReverseInPlace(twMonoIdx) - - return &FourierEvaluator[T]{ - degree: N, - q: Q, - qInv: QInv, - - tw: tw, - twInv: twInv, - twMono: twMono, - twMonoIdx: twMonoIdx, - - buffer: newFourierBuffer[T](N), - } -} - -// genTwiddleFactors generates twiddle factors for FFT. -func genTwiddleFactors(N int) (tw, twInv []complex128) { - wNj := make([]complex128, N/2) - wNjInv := make([]complex128, N/2) - for j := 0; j < N/2; j++ { - e := -2 * math.Pi * float64(j) / float64(N) - wNj[j] = cmplx.Exp(complex(0, e)) - wNjInv[j] = cmplx.Exp(-complex(0, e)) - } - vec.BitReverseInPlace(wNj) - vec.BitReverseInPlace(wNjInv) - - logN := num.Log2(N) - w4Nj := make([]complex128, logN) - w4NjInv := make([]complex128, logN) - for j := 0; j < logN; j++ { - e := 2 * math.Pi * math.Exp2(float64(j)) / float64(4*N) - w4Nj[j] = cmplx.Exp(complex(0, e)) - w4NjInv[j] = cmplx.Exp(-complex(0, e)) - } - - tw = make([]complex128, N) - twInv = make([]complex128, N) - - w, t := 0, logN - for m := 1; m < N; m <<= 1 { - t-- - for i := 0; i < m; i++ { - tw[w] = wNj[i] * w4Nj[t] - w++ - } - } - - w, t = 0, 0 - for m := N; m > 1; m >>= 1 { - for i := 0; i < m/2; i++ { - twInv[w] = wNjInv[i] * w4NjInv[t] - w++ - } - t++ - } - twInv[w-1] /= complex(float64(N), 0) - - return tw, twInv -} - -// newFourierBuffer allocates an empty fourierBuffer. -func newFourierBuffer[T num.Integer](N int) fourierBuffer[T] { - return fourierBuffer[T]{ - fp: NewFourierPoly(N), - fpInv: NewFourierPoly(N), - fpMul: NewFourierPoly(N), - pSplit: NewPoly[T](N), - fpSplit: NewFourierPoly(N), - } -} - -// ShallowCopy returns a shallow copy of this FourierEvaluator. -// Returned FourierEvaluator is safe for concurrent use. -func (f *FourierEvaluator[T]) ShallowCopy() *FourierEvaluator[T] { - return &FourierEvaluator[T]{ - degree: f.degree, - q: f.q, - qInv: f.qInv, - - tw: f.tw, - twInv: f.twInv, - twMono: f.twMono, - twMonoIdx: f.twMonoIdx, - - buffer: newFourierBuffer[T](f.degree), - } -} - -// Degree returns the degree of polynomial that the evaluator can handle. -func (f *FourierEvaluator[T]) Degree() int { - return f.degree -} - -// NewPoly allocates an empty polynomial with the same degree as the evaluator. -func (f *FourierEvaluator[T]) NewPoly() Poly[T] { - return Poly[T]{Coeffs: make([]T, f.degree)} -} - -// NewFourierPoly allocates an empty fourier polynomial with the same degree as the evaluator. -func (f *FourierEvaluator[T]) NewFourierPoly() FourierPoly { - return FourierPoly{Coeffs: make([]float64, f.degree)} -} diff --git a/math/poly/fourier_ops.go b/math/poly/fourier_ops.go index 95b511a..cee082c 100644 --- a/math/poly/fourier_ops.go +++ b/math/poly/fourier_ops.go @@ -1,395 +1,131 @@ package poly -import "github.com/sp301415/tfhe-go/math/num" - -// Round returns round(fp0). -func (f *FourierEvaluator[T]) Round(fp0 FourierPoly) FourierPoly { - fpOut := f.NewFourierPoly() - f.RoundAssign(fp0, fpOut) - return fpOut -} - -// RoundAssign computes fpOut = round(fp0). -func (f *FourierEvaluator[T]) RoundAssign(fp0, fpOut FourierPoly) { - roundCmplxAssign(fp0.Coeffs, fpOut.Coeffs) -} - -// Add returns fp0 + fp1. -func (f *FourierEvaluator[T]) Add(fp0, fp1 FourierPoly) FourierPoly { - fpOut := f.NewFourierPoly() - f.AddAssign(fp0, fp1, fpOut) +// AddFourier returns fp0 + fp1. +func (e *Evaluator[T]) AddFourier(fp0, fp1 FourierPoly) FourierPoly { + fpOut := e.NewFourierPoly() + e.AddFourierAssign(fp0, fp1, fpOut) return fpOut } -// AddAssign computes fpOut = fp0 + fp1. -func (f *FourierEvaluator[T]) AddAssign(fp0, fp1, fpOut FourierPoly) { +// AddFourierAssign computes fpOut = fp0 + fp1. +func (e *Evaluator[T]) AddFourierAssign(fp0, fp1, fpOut FourierPoly) { addCmplxAssign(fp0.Coeffs, fp1.Coeffs, fpOut.Coeffs) } -// Sub returns fp0 - fp1. -func (f *FourierEvaluator[T]) Sub(fp0, fp1 FourierPoly) FourierPoly { - fpOut := f.NewFourierPoly() - f.SubAssign(fp0, fp1, fpOut) +// SubFourier returns fp0 - fp1. +func (e *Evaluator[T]) SubFourier(fp0, fp1 FourierPoly) FourierPoly { + fpOut := e.NewFourierPoly() + e.SubFourierAssign(fp0, fp1, fpOut) return fpOut } -// SubAssign computes fpOut = fp0 - fp1. -func (f *FourierEvaluator[T]) SubAssign(fp0, fp1, fpOut FourierPoly) { +// SubFourierAssign computes fpOut = fp0 - fp1. +func (e *Evaluator[T]) SubFourierAssign(fp0, fp1, fpOut FourierPoly) { subCmplxAssign(fp0.Coeffs, fp1.Coeffs, fpOut.Coeffs) } -// Neg returns -fp0. -func (f *FourierEvaluator[T]) Neg(fp0 FourierPoly) FourierPoly { - fpOut := f.NewFourierPoly() - f.NegAssign(fp0, fpOut) +// NegFourier returns -fp0. +func (e *Evaluator[T]) NegFourier(fp0 FourierPoly) FourierPoly { + fpOut := e.NewFourierPoly() + e.NegFourierAssign(fp0, fpOut) return fpOut } -// NegAssign computes fpOut = -fp0. -func (f *FourierEvaluator[T]) NegAssign(fp0, fpOut FourierPoly) { +// NegFourierAssign computes fpOut = -fp0. +func (e *Evaluator[T]) NegFourierAssign(fp0, fpOut FourierPoly) { negCmplxAssign(fp0.Coeffs, fpOut.Coeffs) } -// FloatMul returns c * fp0. -func (f *FourierEvaluator[T]) FloatMul(fp0 FourierPoly, c float64) FourierPoly { - fpOut := f.NewFourierPoly() - f.FloatMulAssign(fp0, c, fpOut) +// FloatMulFourier returns c * fp0. +func (e *Evaluator[T]) FloatMulFourier(fp0 FourierPoly, c float64) FourierPoly { + fpOut := e.NewFourierPoly() + e.FloatMulFourierAssign(fp0, c, fpOut) return fpOut } -// FloatMulAssign computes fpOut = c * fp0. -func (f *FourierEvaluator[T]) FloatMulAssign(fp0 FourierPoly, c float64, fpOut FourierPoly) { +// FloatMulFourierAssign computes fpOut = c * fp0. +func (e *Evaluator[T]) FloatMulFourierAssign(fp0 FourierPoly, c float64, fpOut FourierPoly) { floatMulCmplxAssign(fp0.Coeffs, c, fpOut.Coeffs) } -// FloatMulAddAssign computes fpOut += c * fp0. -func (f *FourierEvaluator[T]) FloatMulAddAssign(fp0 FourierPoly, c float64, fpOut FourierPoly) { +// FloatMulAddFourierAssign computes fpOut += c * fp0. +func (e *Evaluator[T]) FloatMulAddFourierAssign(fp0 FourierPoly, c float64, fpOut FourierPoly) { floatMulAddCmplxAssign(fp0.Coeffs, c, fpOut.Coeffs) } -// FloatMulSubAssign computes fpOut -= c * fp0. -func (f *FourierEvaluator[T]) FloatMulSubAssign(fp0 FourierPoly, c float64, fpOut FourierPoly) { +// FloatMulSubFourierAssign computes fpOut -= c * fp0. +func (e *Evaluator[T]) FloatMulSubFourierAssign(fp0 FourierPoly, c float64, fpOut FourierPoly) { floatMulSubCmplxAssign(fp0.Coeffs, c, fpOut.Coeffs) } -// CmplxMul returns c * fp0. -func (f *FourierEvaluator[T]) CmplxMul(fp0 FourierPoly, c complex128) FourierPoly { - fpOut := f.NewFourierPoly() - f.CmplxMulAssign(fp0, c, fpOut) +// CmplxMulFourier returns c * fp0. +func (e *Evaluator[T]) CmplxMulFourier(fp0 FourierPoly, c complex128) FourierPoly { + fpOut := e.NewFourierPoly() + e.CmplxMulFourierAssign(fp0, c, fpOut) return fpOut } -// CmplxMulAssign computes fpOut = c * fp0. -func (f *FourierEvaluator[T]) CmplxMulAssign(fp0 FourierPoly, c complex128, fpOut FourierPoly) { +// CmplxMulFourierAssign computes fpOut = c * fp0. +func (e *Evaluator[T]) CmplxMulFourierAssign(fp0 FourierPoly, c complex128, fpOut FourierPoly) { cmplxMulCmplxAssign(fp0.Coeffs, c, fpOut.Coeffs) } -// CmplxMulAddAssign computes fpOut += c * fp0. -func (f *FourierEvaluator[T]) CmplxMulAddAssign(fp0 FourierPoly, c complex128, fpOut FourierPoly) { +// CmplxMulAddFourierAssign computes fpOut += c * fp0. +func (e *Evaluator[T]) CmplxMulAddFourierAssign(fp0 FourierPoly, c complex128, fpOut FourierPoly) { cmplxMulAddCmplxAssign(fp0.Coeffs, c, fpOut.Coeffs) } -// CmplxMulSubAssign computes fpOut -= c * fp0. -func (f *FourierEvaluator[T]) CmplxMulSubAssign(fp0 FourierPoly, c complex128, fpOut FourierPoly) { +// CmplxMulSubFourierAssign computes fpOut -= c * fp0. +func (e *Evaluator[T]) CmplxMulSubFourierAssign(fp0 FourierPoly, c complex128, fpOut FourierPoly) { cmplxMulSubCmplxAssign(fp0.Coeffs, c, fpOut.Coeffs) } -// Mul returns fp0 * fp1. -func (f *FourierEvaluator[T]) Mul(fp0, fp1 FourierPoly) FourierPoly { - fpOut := f.NewFourierPoly() - f.MulAssign(fp0, fp1, fpOut) +// MulFourier returns fp0 * fp1. +func (e *Evaluator[T]) MulFourier(fp0, fp1 FourierPoly) FourierPoly { + fpOut := e.NewFourierPoly() + e.MulFourierAssign(fp0, fp1, fpOut) return fpOut } -// MulAssign computes fpOut = fp0 * fp1. -func (f *FourierEvaluator[T]) MulAssign(fp0, fp1, fpOut FourierPoly) { +// MulFourierAssign computes fpOut = fp0 * fp1. +func (e *Evaluator[T]) MulFourierAssign(fp0, fp1, fpOut FourierPoly) { elementWiseMulCmplxAssign(fp0.Coeffs, fp1.Coeffs, fpOut.Coeffs) } -// MulAddAssign computes fpOut += fp0 * fp1. -func (f *FourierEvaluator[T]) MulAddAssign(fp0, fp1, fpOut FourierPoly) { +// MulAddFourierAssign computes fpOut += fp0 * fp1. +func (e *Evaluator[T]) MulAddFourierAssign(fp0, fp1, fpOut FourierPoly) { elementWiseMulAddCmplxAssign(fp0.Coeffs, fp1.Coeffs, fpOut.Coeffs) } -// MulSubAssign computes fpOut -= fp0 * fp1. -func (f *FourierEvaluator[T]) MulSubAssign(fp0, fp1, fpOut FourierPoly) { +// MulSubFourierAssign computes fpOut -= fp0 * fp1. +func (e *Evaluator[T]) MulSubFourierAssign(fp0, fp1, fpOut FourierPoly) { elementWiseMulSubCmplxAssign(fp0.Coeffs, fp1.Coeffs, fpOut.Coeffs) } -// PolyMul returns p * fp0 as FourierPoly. -func (f *FourierEvaluator[T]) PolyMul(fp0 FourierPoly, p Poly[T]) FourierPoly { - fpOut := f.NewFourierPoly() - f.PolyMulAssign(fp0, p, fpOut) +// PolyMulFourier returns p * fp0 as FourierPoly. +func (e *Evaluator[T]) PolyMulFourier(fp0 FourierPoly, p Poly[T]) FourierPoly { + fpOut := e.NewFourierPoly() + e.PolyMulFourierAssign(fp0, p, fpOut) return fpOut } -// PolyMulAssign computes fpOut = p * fp0. -func (f *FourierEvaluator[T]) PolyMulAssign(fp0 FourierPoly, p Poly[T], fpOut FourierPoly) { - f.ToFourierPolyAssign(p, f.buffer.fpMul) - - elementWiseMulCmplxAssign(fp0.Coeffs, f.buffer.fpMul.Coeffs, fpOut.Coeffs) -} - -// PolyMulAddAssign computes fpOut += p * fp0. -func (f *FourierEvaluator[T]) PolyMulAddAssign(fp0 FourierPoly, p Poly[T], fpOut FourierPoly) { - f.ToFourierPolyAssign(p, f.buffer.fpMul) - - elementWiseMulAddCmplxAssign(fp0.Coeffs, f.buffer.fpMul.Coeffs, fpOut.Coeffs) -} - -// PolyMulSubAssign computes fpOut -= p * fp0. -func (f *FourierEvaluator[T]) PolyMulSubAssign(fp0 FourierPoly, p Poly[T], fpOut FourierPoly) { - f.ToFourierPolyAssign(p, f.buffer.fpMul) - - elementWiseMulSubCmplxAssign(fp0.Coeffs, f.buffer.fpMul.Coeffs, fpOut.Coeffs) -} - -// PolyMulBinary returns p * fp0 as Poly, assuming fp0 is a binary polynomial. -func (f *FourierEvaluator[T]) PolyMulBinary(fp0 FourierPoly, p Poly[T]) Poly[T] { - pOut := NewPoly[T](f.degree) - f.PolyMulBinaryAssign(fp0, p, pOut) - return pOut -} - -// PolyMulBinaryAssign computes pOut = p * fp0, assuming fp0 is a binary polynomial. -// -// p and pOut should not overlap. -func (f *FourierEvaluator[T]) PolyMulBinaryAssign(fp0 FourierPoly, p Poly[T], pOut Poly[T]) { - var splitBits T = 22 - - var z T - switch any(z).(type) { - case int8, uint8, int16, uint16: - f.ToFourierPolyAssign(p, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, pOut) - - case int32, int64, int: - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] % (1 << splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, pOut) - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = (p.Coeffs[i] / (1 << splitBits)) % (1 << splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << splitBits - } - - if num.SizeT[T]() < 64 { - return - } - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] / (1 << (2 * splitBits)) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << (2 * splitBits) - } +// PolyMulFourierAssign computes fpOut = p * fp0. +func (e *Evaluator[T]) PolyMulFourierAssign(fp0 FourierPoly, p Poly[T], fpOut FourierPoly) { + e.ToFourierPolyAssign(p, e.buffer.fpMul) - case uint32, uint64, uint: - var splitMask T = 1<> splitBits) & splitMask - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << splitBits - } - - if num.SizeT[T]() < 64 { - return - } - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] >> (2 * splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << (2 * splitBits) - } - } + elementWiseMulCmplxAssign(fp0.Coeffs, e.buffer.fpMul.Coeffs, fpOut.Coeffs) } -// PolyMulBinaryAddAssign computes pOut += p * fp0, assuming fp0 is a binary polynomial. -// -// p and pOut should not overlap. -func (f *FourierEvaluator[T]) PolyMulBinaryAddAssign(fp0 FourierPoly, p Poly[T], pOut Poly[T]) { - var splitBits T = 22 - - var z T - switch any(z).(type) { - case int8, uint8, int16, uint16: - f.ToFourierPolyAssign(p, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAddAssignUnsafe(f.buffer.fpSplit, pOut) - - case int32, int64, int: - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] % (1 << splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAddAssignUnsafe(f.buffer.fpSplit, pOut) - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = (p.Coeffs[i] / (1 << splitBits)) % (1 << splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << splitBits - } - - if num.SizeT[T]() < 64 { - return - } - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] / (1 << (2 * splitBits)) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << (2 * splitBits) - } - - case uint32, uint64, uint: - var splitMask T = 1<> splitBits) & splitMask - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << splitBits - } - - if num.SizeT[T]() < 64 { - return - } - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] >> (2 * splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] += f.buffer.pSplit.Coeffs[i] << (2 * splitBits) - } - } + elementWiseMulAddCmplxAssign(fp0.Coeffs, e.buffer.fpMul.Coeffs, fpOut.Coeffs) } -// PolyMulBinarySubAssign computes pOut -= p * fp0, assuming fp0 is a binary polynomial. -// -// p and pOut should not overlap. -func (f *FourierEvaluator[T]) PolyMulBinarySubAssign(fp0 FourierPoly, p Poly[T], pOut Poly[T]) { - var splitBits T = 22 - - var z T - switch any(z).(type) { - case int8, uint8, int16, uint16: - f.ToFourierPolyAssign(p, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolySubAssignUnsafe(f.buffer.fpSplit, pOut) - - case int32, int64, int: - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] % (1 << splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolySubAssignUnsafe(f.buffer.fpSplit, pOut) - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = (p.Coeffs[i] / (1 << splitBits)) % (1 << splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] -= f.buffer.pSplit.Coeffs[i] << splitBits - } - - if num.SizeT[T]() < 64 { - return - } - - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] / (1 << (2 * splitBits)) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] -= f.buffer.pSplit.Coeffs[i] << (2 * splitBits) - } - - case uint32, uint64, uint: - var splitMask T = 1<> splitBits) & splitMask - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] -= f.buffer.pSplit.Coeffs[i] << splitBits - } - - if num.SizeT[T]() < 64 { - return - } +// PolyMulSubFourierAssign computes fpOut -= p * fp0. +func (e *Evaluator[T]) PolyMulSubFourierAssign(fp0 FourierPoly, p Poly[T], fpOut FourierPoly) { + e.ToFourierPolyAssign(p, e.buffer.fpMul) - for i := 0; i < f.degree; i++ { - f.buffer.pSplit.Coeffs[i] = p.Coeffs[i] >> (2 * splitBits) - } - f.ToFourierPolyAssign(f.buffer.pSplit, f.buffer.fpSplit) - f.MulAssign(f.buffer.fpSplit, fp0, f.buffer.fpSplit) - f.ToPolyAssignUnsafe(f.buffer.fpSplit, f.buffer.pSplit) - for i := 0; i < f.degree; i++ { - pOut.Coeffs[i] -= f.buffer.pSplit.Coeffs[i] << (2 * splitBits) - } - } + elementWiseMulSubCmplxAssign(fp0.Coeffs, e.buffer.fpMul.Coeffs, fpOut.Coeffs) } diff --git a/math/poly/fourier_transform.go b/math/poly/fourier_transform.go index bc1a60f..d10d419 100644 --- a/math/poly/fourier_transform.go +++ b/math/poly/fourier_transform.go @@ -1,58 +1,58 @@ package poly // ToFourierPoly transforms Poly to FourierPoly. -func (f *FourierEvaluator[T]) ToFourierPoly(p Poly[T]) FourierPoly { - fpOut := NewFourierPoly(f.degree) - f.ToFourierPolyAssign(p, fpOut) +func (e *Evaluator[T]) ToFourierPoly(p Poly[T]) FourierPoly { + fpOut := NewFourierPoly(e.degree) + e.ToFourierPolyAssign(p, fpOut) return fpOut } // ToFourierPolyAssign transforms Poly to FourierPoly and writes it to fpOut. -func (f *FourierEvaluator[T]) ToFourierPolyAssign(p Poly[T], fpOut FourierPoly) { +func (e *Evaluator[T]) ToFourierPolyAssign(p Poly[T], fpOut FourierPoly) { convertPolyToFourierPolyAssign(p.Coeffs, fpOut.Coeffs) - fftInPlace(fpOut.Coeffs, f.tw) + fftInPlace(fpOut.Coeffs, e.tw) } // ToFourierPolyAddAssign transforms Poly to FourierPoly and adds it to fpOut. -func (f *FourierEvaluator[T]) ToFourierPolyAddAssign(p Poly[T], fpOut FourierPoly) { - convertPolyToFourierPolyAssign(p.Coeffs, f.buffer.fp.Coeffs) - fftInPlace(f.buffer.fp.Coeffs, f.tw) - addCmplxAssign(fpOut.Coeffs, f.buffer.fp.Coeffs, fpOut.Coeffs) +func (e *Evaluator[T]) ToFourierPolyAddAssign(p Poly[T], fpOut FourierPoly) { + convertPolyToFourierPolyAssign(p.Coeffs, e.buffer.fp.Coeffs) + fftInPlace(e.buffer.fp.Coeffs, e.tw) + addCmplxAssign(fpOut.Coeffs, e.buffer.fp.Coeffs, fpOut.Coeffs) } // ToFourierPolySubAssign transforms Poly to FourierPoly and subtracts it from fpOut. -func (f *FourierEvaluator[T]) ToFourierPolySubAssign(p Poly[T], fpOut FourierPoly) { - convertPolyToFourierPolyAssign(p.Coeffs, f.buffer.fp.Coeffs) - fftInPlace(f.buffer.fp.Coeffs, f.tw) - subCmplxAssign(fpOut.Coeffs, f.buffer.fp.Coeffs, fpOut.Coeffs) +func (e *Evaluator[T]) ToFourierPolySubAssign(p Poly[T], fpOut FourierPoly) { + convertPolyToFourierPolyAssign(p.Coeffs, e.buffer.fp.Coeffs) + fftInPlace(e.buffer.fp.Coeffs, e.tw) + subCmplxAssign(fpOut.Coeffs, e.buffer.fp.Coeffs, fpOut.Coeffs) } // MonomialToFourierPoly transforms X^d to FourierPoly. // // d should be positive. -func (f *FourierEvaluator[T]) MonomialToFourierPoly(d int) FourierPoly { - fpOut := NewFourierPoly(f.degree) - f.MonomialToFourierPolyAssign(d, fpOut) +func (e *Evaluator[T]) MonomialToFourierPoly(d int) FourierPoly { + fpOut := NewFourierPoly(e.degree) + e.MonomialToFourierPolyAssign(d, fpOut) return fpOut } // MonomialToFourierPolyAssign transforms X^d to FourierPoly and writes it to fpOut. -func (f *FourierEvaluator[T]) MonomialToFourierPolyAssign(d int, fpOut FourierPoly) { - d &= 2*f.degree - 1 - for j, jj := 0, 0; j < f.degree; j, jj = j+8, jj+4 { - c0 := f.twMono[(f.twMonoIdx[jj+0]*d)&(2*f.degree-1)] +func (e *Evaluator[T]) MonomialToFourierPolyAssign(d int, fpOut FourierPoly) { + d &= 2*e.degree - 1 + for j, jj := 0, 0; j < e.degree; j, jj = j+8, jj+4 { + c0 := e.twMono[(e.twMonoIdx[jj+0]*d)&(2*e.degree-1)] fpOut.Coeffs[j+0] = real(c0) fpOut.Coeffs[j+4] = imag(c0) - c1 := f.twMono[(f.twMonoIdx[jj+1]*d)&(2*f.degree-1)] + c1 := e.twMono[(e.twMonoIdx[jj+1]*d)&(2*e.degree-1)] fpOut.Coeffs[j+1] = real(c1) fpOut.Coeffs[j+5] = imag(c1) - c2 := f.twMono[(f.twMonoIdx[jj+2]*d)&(2*f.degree-1)] + c2 := e.twMono[(e.twMonoIdx[jj+2]*d)&(2*e.degree-1)] fpOut.Coeffs[j+2] = real(c2) fpOut.Coeffs[j+6] = imag(c2) - c3 := f.twMono[(f.twMonoIdx[jj+3]*d)&(2*f.degree-1)] + c3 := e.twMono[(e.twMonoIdx[jj+3]*d)&(2*e.degree-1)] fpOut.Coeffs[j+3] = real(c3) fpOut.Coeffs[j+7] = imag(c3) } @@ -61,72 +61,72 @@ func (f *FourierEvaluator[T]) MonomialToFourierPolyAssign(d int, fpOut FourierPo // MonomialSubOneToFourierPoly transforms X^d-1 to FourierPoly. // // d should be positive. -func (f *FourierEvaluator[T]) MonomialSubOneToFourierPoly(d int) FourierPoly { - fpOut := NewFourierPoly(f.degree) - f.MonomialSubOneToFourierPolyAssign(d, fpOut) +func (e *Evaluator[T]) MonomialSubOneToFourierPoly(d int) FourierPoly { + fpOut := NewFourierPoly(e.degree) + e.MonomialSubOneToFourierPolyAssign(d, fpOut) return fpOut } // MonomialSubOneToFourierPolyAssign transforms X^d-1 to FourierPoly and writes it to fpOut. -func (f *FourierEvaluator[T]) MonomialSubOneToFourierPolyAssign(d int, fpOut FourierPoly) { - d &= 2*f.degree - 1 - for j, jj := 0, 0; j < f.degree; j, jj = j+8, jj+4 { - c0 := f.twMono[(f.twMonoIdx[jj+0]*d)&(2*f.degree-1)] +func (e *Evaluator[T]) MonomialSubOneToFourierPolyAssign(d int, fpOut FourierPoly) { + d &= 2*e.degree - 1 + for j, jj := 0, 0; j < e.degree; j, jj = j+8, jj+4 { + c0 := e.twMono[(e.twMonoIdx[jj+0]*d)&(2*e.degree-1)] fpOut.Coeffs[j+0] = real(c0) - 1 fpOut.Coeffs[j+4] = imag(c0) - c1 := f.twMono[(f.twMonoIdx[jj+1]*d)&(2*f.degree-1)] + c1 := e.twMono[(e.twMonoIdx[jj+1]*d)&(2*e.degree-1)] fpOut.Coeffs[j+1] = real(c1) - 1 fpOut.Coeffs[j+5] = imag(c1) - c2 := f.twMono[(f.twMonoIdx[jj+2]*d)&(2*f.degree-1)] + c2 := e.twMono[(e.twMonoIdx[jj+2]*d)&(2*e.degree-1)] fpOut.Coeffs[j+2] = real(c2) - 1 fpOut.Coeffs[j+6] = imag(c2) - c3 := f.twMono[(f.twMonoIdx[jj+3]*d)&(2*f.degree-1)] + c3 := e.twMono[(e.twMonoIdx[jj+3]*d)&(2*e.degree-1)] fpOut.Coeffs[j+3] = real(c3) - 1 fpOut.Coeffs[j+7] = imag(c3) } } // ToPoly transforms FourierPoly to Poly. -func (f *FourierEvaluator[T]) ToPoly(fp FourierPoly) Poly[T] { - pOut := NewPoly[T](f.degree) - f.ToPolyAssign(fp, pOut) +func (e *Evaluator[T]) ToPoly(fp FourierPoly) Poly[T] { + pOut := NewPoly[T](e.degree) + e.ToPolyAssign(fp, pOut) return pOut } // ToPolyAssign transforms FourierPoly to Poly and writes it to pOut. -func (f *FourierEvaluator[T]) ToPolyAssign(fp FourierPoly, pOut Poly[T]) { - f.buffer.fpInv.CopyFrom(fp) - invFFTInPlace(f.buffer.fpInv.Coeffs, f.twInv) - floatModQInPlace(f.buffer.fpInv.Coeffs, f.q, f.qInv) - convertFourierPolyToPolyAssign(f.buffer.fpInv.Coeffs, pOut.Coeffs) +func (e *Evaluator[T]) ToPolyAssign(fp FourierPoly, pOut Poly[T]) { + e.buffer.fpInv.CopyFrom(fp) + invFFTInPlace(e.buffer.fpInv.Coeffs, e.twInv) + floatModQInPlace(e.buffer.fpInv.Coeffs, e.q, e.qInv) + convertFourierPolyToPolyAssign(e.buffer.fpInv.Coeffs, pOut.Coeffs) } // ToPolyAddAssign transforms FourierPoly to Poly and adds it to pOut. -func (f *FourierEvaluator[T]) ToPolyAddAssign(fp FourierPoly, pOut Poly[T]) { - f.buffer.fpInv.CopyFrom(fp) - invFFTInPlace(f.buffer.fpInv.Coeffs, f.twInv) - floatModQInPlace(f.buffer.fpInv.Coeffs, f.q, f.qInv) - convertFourierPolyToPolyAddAssign(f.buffer.fpInv.Coeffs, pOut.Coeffs) +func (e *Evaluator[T]) ToPolyAddAssign(fp FourierPoly, pOut Poly[T]) { + e.buffer.fpInv.CopyFrom(fp) + invFFTInPlace(e.buffer.fpInv.Coeffs, e.twInv) + floatModQInPlace(e.buffer.fpInv.Coeffs, e.q, e.qInv) + convertFourierPolyToPolyAddAssign(e.buffer.fpInv.Coeffs, pOut.Coeffs) } // ToPolySubAssign transforms FourierPoly to Poly and subtracts it from pOut. -func (f *FourierEvaluator[T]) ToPolySubAssign(fp FourierPoly, pOut Poly[T]) { - f.buffer.fpInv.CopyFrom(fp) - invFFTInPlace(f.buffer.fpInv.Coeffs, f.twInv) - floatModQInPlace(f.buffer.fpInv.Coeffs, f.q, f.qInv) - convertFourierPolyToPolySubAssign(f.buffer.fpInv.Coeffs, pOut.Coeffs) +func (e *Evaluator[T]) ToPolySubAssign(fp FourierPoly, pOut Poly[T]) { + e.buffer.fpInv.CopyFrom(fp) + invFFTInPlace(e.buffer.fpInv.Coeffs, e.twInv) + floatModQInPlace(e.buffer.fpInv.Coeffs, e.q, e.qInv) + convertFourierPolyToPolySubAssign(e.buffer.fpInv.Coeffs, pOut.Coeffs) } // ToPolyAssignUnsafe transforms FourierPoly to Poly and writes it to pOut. // // This method is slightly faster than ToPolyAssign, but it modifies fp directly. // Use it only if you don't need fp after this method (e.g. fp is a buffer). -func (f *FourierEvaluator[T]) ToPolyAssignUnsafe(fp FourierPoly, pOut Poly[T]) { - invFFTInPlace(fp.Coeffs, f.twInv) - floatModQInPlace(fp.Coeffs, f.q, f.qInv) +func (e *Evaluator[T]) ToPolyAssignUnsafe(fp FourierPoly, pOut Poly[T]) { + invFFTInPlace(fp.Coeffs, e.twInv) + floatModQInPlace(fp.Coeffs, e.q, e.qInv) convertFourierPolyToPolyAssign(fp.Coeffs, pOut.Coeffs) } @@ -134,9 +134,9 @@ func (f *FourierEvaluator[T]) ToPolyAssignUnsafe(fp FourierPoly, pOut Poly[T]) { // // This method is slightly faster than ToPolyAddAssign, but it modifies fp directly. // Use it only if you don't need fp after this method (e.g. fp is a buffer). -func (f *FourierEvaluator[T]) ToPolyAddAssignUnsafe(fp FourierPoly, pOut Poly[T]) { - invFFTInPlace(fp.Coeffs, f.twInv) - floatModQInPlace(fp.Coeffs, f.q, f.qInv) +func (e *Evaluator[T]) ToPolyAddAssignUnsafe(fp FourierPoly, pOut Poly[T]) { + invFFTInPlace(fp.Coeffs, e.twInv) + floatModQInPlace(fp.Coeffs, e.q, e.qInv) convertFourierPolyToPolyAddAssign(fp.Coeffs, pOut.Coeffs) } @@ -144,8 +144,8 @@ func (f *FourierEvaluator[T]) ToPolyAddAssignUnsafe(fp FourierPoly, pOut Poly[T] // // This method is slightly faster than ToPolySubAssign, but it modifies fp directly. // Use it only if you don't need fp after this method (e.g. fp is a buffer). -func (f *FourierEvaluator[T]) ToPolySubAssignUnsafe(fp FourierPoly, pOut Poly[T]) { - invFFTInPlace(fp.Coeffs, f.twInv) - floatModQInPlace(fp.Coeffs, f.q, f.qInv) +func (e *Evaluator[T]) ToPolySubAssignUnsafe(fp FourierPoly, pOut Poly[T]) { + invFFTInPlace(fp.Coeffs, e.twInv) + floatModQInPlace(fp.Coeffs, e.q, e.qInv) convertFourierPolyToPolySubAssign(fp.Coeffs, pOut.Coeffs) } diff --git a/math/poly/karatsuba.go b/math/poly/karatsuba.go deleted file mode 100644 index 98f06b5..0000000 --- a/math/poly/karatsuba.go +++ /dev/null @@ -1,170 +0,0 @@ -package poly - -import ( - "github.com/sp301415/tfhe-go/math/num" -) - -const ( - // karatsubaRecurseThreshold is the minimum degree of plaintext to perform karatsuba multiplication. - // If polynomial's degree equals this value, then the recursive algorithm switches to textbook multiplication. - karatsubaRecurseThreshold = 64 -) - -// karatsubaBuffer contains vuffer values for each karatsuba multiplication. -type karatsubaBuffer[T num.Integer] struct { - // a0 = p0 + q0 - a0 []T - // a1 = p1 + q1 - a1 []T - // d0 = karatsuba(p0, q0) - d0 []T - // d1 = karatsuba(p1, q1) - d1 []T - // d2 = karatsuba(a0, a1) - d2 []T -} - -// newKaratsubaBuffer allocates a slice of karatsuba buffer. -// Each value of the buffer can be accessed with karatsubaTreeIndex. -func newKaratsubaBuffer[T num.Integer](N int) []karatsubaBuffer[T] { - fullDepth := num.Log2(N / karatsubaRecurseThreshold) - buff := make([]karatsubaBuffer[T], fullDepth) - for i := 0; i < fullDepth; i++ { - // In depth i, karatsuba inputs have size N / 2^i - buff[i] = karatsubaBuffer[T]{ - a0: make([]T, N>>(i+1)), - a1: make([]T, N>>(i+1)), - d0: make([]T, N>>i), - d1: make([]T, N>>i), - d2: make([]T, N>>i), - } - } - - return buff -} - -// mulNaive multiplies two polynomials using schoolbook method, -// taking O(N^2) time. -func (e *Evaluator[T]) mulNaive(p0, p1 Poly[T]) Poly[T] { - pOut := e.NewPoly() - e.mulNaiveAssign(p0, p1, pOut) - return pOut -} - -// mulNaiveAssign multiplies two polynomials using schoolbook method, -// taking O(N^2) time. -func (e *Evaluator[T]) mulNaiveAssign(p0, p1, pOut Poly[T]) { - for i := 0; i < e.degree; i++ { - pOut.Coeffs[i] = p0.Coeffs[i] * p1.Coeffs[0] - } - - for i := 0; i < e.degree; i++ { - for j := 1; j < e.degree; j++ { - if i+j < e.degree { - pOut.Coeffs[i+j] += p0.Coeffs[i] * p1.Coeffs[j] - } else { - pOut.Coeffs[i+j-e.degree] -= p0.Coeffs[i] * p1.Coeffs[j] - } - } - } -} - -// mulKaratsubaAssign multiplies two polynomials using recursive karatsuba multiplication, -// taking O(N^1.58) time. -func (e *Evaluator[T]) mulKaratsubaAssign(p, q, pOut Poly[T]) { - // Implementation Note: - // Seems like using range-based loops are faster than regular loops. - - N := e.degree - karatsubaBuffer := newKaratsubaBuffer[T](N) - - buff := karatsubaBuffer[0] - - // p = p0 * X^N/2 + p1 - p0, p1 := p.Coeffs[:N/2], p.Coeffs[N/2:] - // q = q0 * X^N/2 + q1 - q0, q1 := q.Coeffs[:N/2], q.Coeffs[N/2:] - - // d0 := p0 * q0 - e.karatsuba(p0, q0, buff.d0, 1, karatsubaBuffer) - // d1 := p1 * q1 - e.karatsuba(p1, q1, buff.d1, 1, karatsubaBuffer) - - // a0 := p0 + p1 - for i := range buff.a0 { - buff.a0[i] = p0[i] + p1[i] - } - // a1 := q0 + q1 - for i := range buff.a1 { - buff.a1[i] = q0[i] + q1[i] - } - - // d2 := (p0 + p1) * (q0 + q1) = p0*q0 + p0*q1 + p1*q0 + p1*q1 = (p0*q1 + p1*q0) + d0 + d1 - e.karatsuba(buff.a0, buff.a1, buff.d2, 1, karatsubaBuffer) - - // pOut := d0 + (d2 - d0 - d1)*X^N/2 + d1*X^N - for i := range pOut.Coeffs { - if i < N/2 { - pOut.Coeffs[i] = buff.d0[i] - (buff.d2[i+N/2] - buff.d0[i+N/2] - buff.d1[i+N/2]) - buff.d1[i] // d0 + (d2 - d0 - d1) - d1: 0 ~ N/2 - } else { - pOut.Coeffs[i] = buff.d0[i] + (buff.d2[i-N/2] - buff.d0[i-N/2] - buff.d1[i-N/2]) - buff.d1[i] // d0 - (d2 - d0 - d1) + d1: N/2 ~ N - } - } -} - -// karatsuba is a recursive subroutine of karatsuba algorithm. -// length of pOut is assumed to be twice of length of p and q. -func (e *Evaluator[T]) karatsuba(p, q, pOut []T, depth int, karatsubaBuffer []karatsubaBuffer[T]) { - N := len(p) - - if N <= karatsubaRecurseThreshold { - // Multiply naively - for i := range pOut { - pOut[i] = 0 - } - - for i := range p { - for j := range q { - pOut[i+j] += p[i] * q[j] - } - } - return - } - - buff := karatsubaBuffer[depth] - - // p = p0 * X^N/2 + p1 - p0, p1 := p[:N/2], p[N/2:] - // q = q0 * X^N/2 + q1 - q0, q1 := q[:N/2], q[N/2:] - - // d0 := p0 * q0 - e.karatsuba(p0, q0, buff.d0, depth+1, karatsubaBuffer) - // d1 := p1 * q1 - e.karatsuba(p1, q1, buff.d1, depth+1, karatsubaBuffer) - - // a0 := p0 + p1 - for i := range buff.a0 { - buff.a0[i] = p0[i] + p1[i] - } - // a1 := q0 + q1 - for i := range buff.a1 { - buff.a1[i] = q0[i] + q1[i] - } - - // d2 := (p0 + p1) * (q0 + q1) = p0*q0 + p0*q1 + p1*q0 + p1*q1 = (p0*q1 + p1*q0) + d0 + d1 - e.karatsuba(buff.a0, buff.a1, buff.d2, depth+1, karatsubaBuffer) - - // pOut := d0 + (d2 - d0 - d1)*X^N/2 + d1*X^N - for i := range pOut { - if i < N { - pOut[i] = buff.d0[i] // d0: 0 ~ N - } - if N <= i { - pOut[i] = buff.d1[i-N] // d1: N ~ 2N - } - if N/2 <= i && i < 3*N/2 { - pOut[i] += buff.d2[i-N/2] - buff.d0[i-N/2] - buff.d1[i-N/2] // d2 -d0 - d1: N/2 ~ 3N/2 - } - } -} diff --git a/math/poly/poly_evaluator.go b/math/poly/poly_evaluator.go index fa3a7cb..a25e159 100644 --- a/math/poly/poly_evaluator.go +++ b/math/poly/poly_evaluator.go @@ -1,7 +1,11 @@ package poly import ( + "math" + "math/cmplx" + "github.com/sp301415/tfhe-go/math/num" + "github.com/sp301415/tfhe-go/math/vec" ) const ( @@ -11,25 +15,68 @@ const ( MinDegree = 1 << 4 // MaxDegree is the maximum degree of polynomial that Evaluator can handle. - // Currently, this is set to 2^30, because [*FourierEvaluator.PolyMulBinary] trick - // supports up to 2^30 degree. - MaxDegree = 1 << 30 + // Currently, the maximum split bits used in [*Evaluator.Mul] is 13. + // For this split, in degree 2^24, the failure probability is less than 2^-73, which is negligible. + MaxDegree = 1 << 24 ) -// Evaluator computes polynomial algorithms over the coefficient domain. +// Evaluator computes polynomial operations over the N-th cyclotomic ring. // // Operations usually take two forms: for example, -// - Add(p0, p1) adds p0, p1, allocates a new polynomial to store the result and returns it. -// - AddAssign(p0, p1, pOut) adds p0, p1 and writes the result to pre-allocated pOut without returning. +// - Op(p0, p1) adds p0, p1, allocates a new polynomial to store the result and returns it. +// - OpAssign(p0, p1, pOut) adds p0, p1 and writes the result to pre-allocated pOut without returning. // -// Note that in most cases, p0, p1, and pOut can overlap. +// Note that in most cases, p0, p1, and fpOut can overlap. // However, for operations that cannot, InPlace methods are implemented separately. // // For performance reasons, most methods in this package don't implement bound checks. // If length mismatch happens, it may panic or produce wrong results. type Evaluator[T num.Integer] struct { - // degree is the degree of polynomial that this evaluator can handle. + // degree is the degree of polynomial that this transformer can handle. degree int + // q is a float64 value of Q. + q float64 + // qInv is a float64 value of 1/Q. + qInv float64 + + // tw holds the twiddle factors for fourier transform. + // This is stored as "long" form, so that access to the factors are contiguous. + // Unlike other complex128 slices, tw is in natural representation. + tw []complex128 + // twInv holds the twiddle factors for inverse fourier transform. + // This is stored as "long" form, so that access to the factors are contiguous. + // Unlike other complex128 slices, twInv is in natural representation. + twInv []complex128 + // twMono holds the twiddle factors for monomial fourier transform. + // Unlike other complex128 slices, twMono is in natural representation. + twMono []complex128 + // twMonoIdx holds the precomputed bit-reversed index for monomial fourier transform. + // Equivalent to BitReverse([-1, 3, 7, ..., 2N-1]). + twMonoIdx []int + + // splitBits is the number of bits to split in [*Evaluator.Mul]. + // + // - If sizeT <= 32, splitBits = 11. + // - If sizeT = 64, splitBits = 13. + splitBits T + // splitCount is the number of splits in [*Evaluator.Mul]. + // + // - If sizeT = 8, splitCount = 1. + // - If sizeT = 16, splitCount = 2. + // - If sizeT = 32, splitCount = 3. + // - If sizeT = 64, splitCount = 5. + splitCount int + // splitBitsBinary is the number of bits to split in [*Evaluator.BinaryFourierMul]. + // + // - If sizeT <= 16, splitBitsBinary = 16. + // - If sizeT >= 32, splitBitsBinary = 26. + splitBitsBinary T + // splitCountBinary is the number of splits in [*Evaluator.BinaryFourierMul]. + // + // - If sizeT <= 16, splitCountBinary = 1. + // - If sizeT = 32, splitCountBinary = 2. + // - If sizeT = 64, splitCountBinary = 3. + splitCountBinary int // buffer holds the buffer values for this Evaluator. buffer evaluationBuffer[T] @@ -37,8 +84,27 @@ type Evaluator[T num.Integer] struct { // evaluationBuffer contains buffer values for Evaluator. type evaluationBuffer[T num.Integer] struct { - // pOut holds the intermediate polynomial in MulAdd/MulSub and Permute type operations. + // pOut holds the intermediate output polynomial for InPlace operations. pOut Poly[T] + + // fp holds the FFT value of p. + fp FourierPoly + // fpInv holds the InvFFT value of fp. + fpInv FourierPoly + + // fpMul holds the FFT value of p in multiplication. + fpMul FourierPoly + + // p0Split holds the split value of p0 in [*Evaluator.Mul]. + p0Split []Poly[T] + // p1Split holds the split value of p1 in [*Evaluator.Mul]. + p1Split []Poly[T] + // fp0Split holds the fourier transformed p0Split. + fp0Split []FourierPoly + // fp1Split holds the fourier transformed p1Split. + fp1Split []FourierPoly + // fpOutSplit holds the fourier transformed pOutSplit. + fpOutSplit []FourierPoly } // NewEvaluator allocates an empty Evaluator with degree N. @@ -54,17 +120,155 @@ func NewEvaluator[T num.Integer](N int) *Evaluator[T] { panic("degree larger than MaxDegree") } + Q := math.Exp2(float64(num.SizeT[T]())) + QInv := math.Exp2(-float64(num.SizeT[T]())) + + tw, twInv := genTwiddleFactors(N / 2) + + twMono := make([]complex128, 2*N) + for j := 0; j < 2*N; j++ { + e := -math.Pi * float64(j) / float64(N) + twMono[j] = cmplx.Exp(complex(0, e)) + } + + twMonoIdx := make([]int, N/2) + twMonoIdx[0] = 2*N - 1 + for i := 1; i < N/2; i++ { + twMonoIdx[i] = 4*i - 1 + } + vec.BitReverseInPlace(twMonoIdx) + + splitBits, splitCount := genSplitParameters[T]() + splitBitsBinary, splitCountBinary := genSplitParametersBinary[T]() + return &Evaluator[T]{ degree: N, + q: Q, + qInv: QInv, + + tw: tw, + twInv: twInv, + twMono: twMono, + twMonoIdx: twMonoIdx, + + splitBits: splitBits, + splitCount: splitCount, + splitBitsBinary: splitBitsBinary, + splitCountBinary: splitCountBinary, + + buffer: newFourierBuffer[T](N), + } +} + +// genTwiddleFactors generates twiddle factors for FFT. +func genTwiddleFactors(N int) (tw, twInv []complex128) { + wNj := make([]complex128, N/2) + wNjInv := make([]complex128, N/2) + for j := 0; j < N/2; j++ { + e := -2 * math.Pi * float64(j) / float64(N) + wNj[j] = cmplx.Exp(complex(0, e)) + wNjInv[j] = cmplx.Exp(-complex(0, e)) + } + vec.BitReverseInPlace(wNj) + vec.BitReverseInPlace(wNjInv) + + logN := num.Log2(N) + w4Nj := make([]complex128, logN) + w4NjInv := make([]complex128, logN) + for j := 0; j < logN; j++ { + e := 2 * math.Pi * math.Exp2(float64(j)) / float64(4*N) + w4Nj[j] = cmplx.Exp(complex(0, e)) + w4NjInv[j] = cmplx.Exp(-complex(0, e)) + } + + tw = make([]complex128, N) + twInv = make([]complex128, N) + + w, t := 0, logN + for m := 1; m < N; m <<= 1 { + t-- + for i := 0; i < m; i++ { + tw[w] = wNj[i] * w4Nj[t] + w++ + } + } + + w, t = 0, 0 + for m := N; m > 1; m >>= 1 { + for i := 0; i < m/2; i++ { + twInv[w] = wNjInv[i] * w4NjInv[t] + w++ + } + t++ + } + twInv[w-1] /= complex(float64(N), 0) + + return tw, twInv +} + +// genSplitParameters generates splitBits and splitCount for [*Evaluator.Mul]. +func genSplitParameters[T num.Integer]() (splitBits T, splitCount int) { + switch num.SizeT[T]() { + case 8: + return 11, 1 + case 16: + return 11, 2 + case 32: + return 11, 3 + case 64: + return 13, 5 + } + return 0, 0 +} - buffer: newEvaluationBuffer[T](N), +// genSplitParametersBinary generates splitBitsBinary and splitCountBinary for [*Evaluator.BinaryFourierMul]. +func genSplitParametersBinary[T num.Integer]() (splitBitsBinary T, splitCountBinary int) { + switch num.SizeT[T]() { + case 8: + return 16, 1 + case 16: + return 16, 1 + case 32: + return 26, 2 + case 64: + return 26, 3 } + return 0, 0 } -// newEvaluationBuffer allocates an empty evaluation buffer. -func newEvaluationBuffer[T num.Integer](N int) evaluationBuffer[T] { +// newFourierBuffer allocates an empty fourierBuffer. +func newFourierBuffer[T num.Integer](N int) evaluationBuffer[T] { + _, splitCount := genSplitParameters[T]() + + p0Split := make([]Poly[T], splitCount) + p1Split := make([]Poly[T], splitCount) + for i := 0; i < splitCount; i++ { + p0Split[i] = NewPoly[T](N) + p1Split[i] = NewPoly[T](N) + } + + fp0Split := make([]FourierPoly, splitCount) + fp1Split := make([]FourierPoly, splitCount) + fpOutSplit := make([]FourierPoly, splitCount) + for i := 0; i < splitCount; i++ { + fp0Split[i] = NewFourierPoly(N) + fp1Split[i] = NewFourierPoly(N) + fpOutSplit[i] = NewFourierPoly(N) + } + return evaluationBuffer[T]{ pOut: NewPoly[T](N), + + fp: NewFourierPoly(N), + fpInv: NewFourierPoly(N), + + fpMul: NewFourierPoly(N), + + p0Split: p0Split, + p1Split: p1Split, + fp0Split: fp0Split, + fp1Split: fp1Split, + fpOutSplit: fpOutSplit, } } @@ -73,8 +277,20 @@ func newEvaluationBuffer[T num.Integer](N int) evaluationBuffer[T] { func (e *Evaluator[T]) ShallowCopy() *Evaluator[T] { return &Evaluator[T]{ degree: e.degree, + q: e.q, + qInv: e.qInv, + + tw: e.tw, + twInv: e.twInv, + twMono: e.twMono, + twMonoIdx: e.twMonoIdx, + + splitBits: e.splitBits, + splitCount: e.splitCount, + splitBitsBinary: e.splitBitsBinary, + splitCountBinary: e.splitCountBinary, - buffer: newEvaluationBuffer[T](e.degree), + buffer: newFourierBuffer[T](e.degree), } } diff --git a/math/poly/poly_mul.go b/math/poly/poly_mul.go new file mode 100644 index 0000000..5d59c0f --- /dev/null +++ b/math/poly/poly_mul.go @@ -0,0 +1,309 @@ +package poly + +import "github.com/sp301415/tfhe-go/math/num" + +// Mul returns p0 * p1. +func (e *Evaluator[T]) Mul(p0, p1 Poly[T]) Poly[T] { + pOut := e.NewPoly() + e.MulAssign(p0, p1, pOut) + return pOut +} + +// MulAssign computes pOut = p0 * p1. +func (e *Evaluator[T]) MulAssign(p0, p1, pOut Poly[T]) { + if e.splitCount == 1 { + e.ToFourierPolyAssign(p0, e.buffer.fp0Split[0]) + e.ToFourierPolyAssign(p1, e.buffer.fp1Split[0]) + e.MulFourierAssign(e.buffer.fp0Split[0], e.buffer.fp1Split[0], e.buffer.fpOutSplit[0]) + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + return + } + + if num.IsSigned[T]() { + var splitChunk T = 1 << e.splitBits + for i := 0; i < e.splitCount; i++ { + var splitLow T = 1 << (i * int(e.splitBits)) + for j := 0; j < e.degree; j++ { + e.buffer.p0Split[i].Coeffs[j] = (p0.Coeffs[j] / splitLow) % splitChunk + e.buffer.p1Split[i].Coeffs[j] = (p1.Coeffs[j] / splitLow) % splitChunk + } + } + } else { + var splitMask T = 1<> splitLowBits) & splitMask + e.buffer.p1Split[i].Coeffs[j] = (p1.Coeffs[j] >> splitLowBits) & splitMask + } + } + } + + for i := 0; i < e.splitCount; i++ { + e.ToFourierPolyAssign(e.buffer.p0Split[i], e.buffer.fp0Split[i]) + e.ToFourierPolyAssign(e.buffer.p1Split[i], e.buffer.fp1Split[i]) + } + + for j := 0; j < e.splitCount; j++ { + e.MulFourierAssign(e.buffer.fp0Split[0], e.buffer.fp1Split[j], e.buffer.fpOutSplit[j]) + } + for i := 1; i < e.splitCount; i++ { + for j := 0; j < e.splitCount-i; j++ { + e.MulAddFourierAssign(e.buffer.fp0Split[i], e.buffer.fp1Split[j], e.buffer.fpOutSplit[i+j]) + } + } + + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + for i := 1; i < e.splitCount; i++ { + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[i], e.buffer.p0Split[i]) + splitLowBits := i * int(e.splitBits) + for j := 0; j < e.degree; j++ { + pOut.Coeffs[j] += e.buffer.p0Split[i].Coeffs[j] << splitLowBits + } + } +} + +// MulAddAssign computes pOut += p0 * p1. +func (e *Evaluator[T]) MulAddAssign(p0, p1, pOut Poly[T]) { + if e.splitCount == 1 { + e.ToFourierPolyAssign(p0, e.buffer.fp0Split[0]) + e.ToFourierPolyAssign(p1, e.buffer.fp1Split[0]) + e.MulFourierAssign(e.buffer.fp0Split[0], e.buffer.fp1Split[0], e.buffer.fpOutSplit[0]) + e.ToPolyAddAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + return + } + + if num.IsSigned[T]() { + var splitChunk T = 1 << e.splitBits + for i := 0; i < e.splitCount; i++ { + var splitLow T = 1 << (i * int(e.splitBits)) + for j := 0; j < e.degree; j++ { + e.buffer.p0Split[i].Coeffs[j] = (p0.Coeffs[j] / splitLow) % splitChunk + e.buffer.p1Split[i].Coeffs[j] = (p1.Coeffs[j] / splitLow) % splitChunk + } + } + } else { + var splitMask T = 1<> splitLowBits) & splitMask + e.buffer.p1Split[i].Coeffs[j] = (p1.Coeffs[j] >> splitLowBits) & splitMask + } + } + } + + for i := 0; i < e.splitCount; i++ { + e.ToFourierPolyAssign(e.buffer.p0Split[i], e.buffer.fp0Split[i]) + e.ToFourierPolyAssign(e.buffer.p1Split[i], e.buffer.fp1Split[i]) + } + + for j := 0; j < e.splitCount; j++ { + e.MulFourierAssign(e.buffer.fp0Split[0], e.buffer.fp1Split[j], e.buffer.fpOutSplit[j]) + } + for i := 1; i < e.splitCount; i++ { + for j := 0; j < e.splitCount-i; j++ { + e.MulAddFourierAssign(e.buffer.fp0Split[i], e.buffer.fp1Split[j], e.buffer.fpOutSplit[i+j]) + } + } + + e.ToPolyAddAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + for i := 1; i < e.splitCount; i++ { + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[i], e.buffer.p0Split[i]) + splitLowBits := i * int(e.splitBits) + for j := 0; j < e.degree; j++ { + pOut.Coeffs[j] += e.buffer.p0Split[i].Coeffs[j] << splitLowBits + } + } +} + +// MulSubAssign computes pOut -= p0 * p1. +func (e *Evaluator[T]) MulSubAssign(p0, p1, pOut Poly[T]) { + if e.splitCount == 1 { + e.ToFourierPolyAssign(p0, e.buffer.fp0Split[0]) + e.ToFourierPolyAssign(p1, e.buffer.fp1Split[0]) + e.MulFourierAssign(e.buffer.fp0Split[0], e.buffer.fp1Split[0], e.buffer.fpOutSplit[0]) + e.ToPolySubAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + return + } + + if num.IsSigned[T]() { + var splitChunk T = 1 << e.splitBits + for i := 0; i < e.splitCount; i++ { + var splitLow T = 1 << (i * int(e.splitBits)) + for j := 0; j < e.degree; j++ { + e.buffer.p0Split[i].Coeffs[j] = (p0.Coeffs[j] / splitLow) % splitChunk + e.buffer.p1Split[i].Coeffs[j] = (p1.Coeffs[j] / splitLow) % splitChunk + } + } + } else { + var splitMask T = 1<> splitLowBits) & splitMask + e.buffer.p1Split[i].Coeffs[j] = (p1.Coeffs[j] >> splitLowBits) & splitMask + } + } + } + + for i := 0; i < e.splitCount; i++ { + e.ToFourierPolyAssign(e.buffer.p0Split[i], e.buffer.fp0Split[i]) + e.ToFourierPolyAssign(e.buffer.p1Split[i], e.buffer.fp1Split[i]) + } + + for j := 0; j < e.splitCount; j++ { + e.MulFourierAssign(e.buffer.fp0Split[0], e.buffer.fp1Split[j], e.buffer.fpOutSplit[j]) + } + for i := 1; i < e.splitCount; i++ { + for j := 0; j < e.splitCount-i; j++ { + e.MulAddFourierAssign(e.buffer.fp0Split[i], e.buffer.fp1Split[j], e.buffer.fpOutSplit[i+j]) + } + } + + e.ToPolySubAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + for i := 1; i < e.splitCount; i++ { + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[i], e.buffer.p0Split[i]) + splitLowBits := i * int(e.splitBits) + for j := 0; j < e.degree; j++ { + pOut.Coeffs[j] -= e.buffer.p0Split[i].Coeffs[j] << splitLowBits + } + } +} + +// BinaryFourierMul returns p0 * bfp, under the assumption that bfp is a binary polynomial. +// This is faster than [*Evaluator.Mul], and the result is exact unlike [*Evaluator.FourierMul]. +func (e *Evaluator[T]) BinaryFourierMul(p0 Poly[T], bfp FourierPoly) Poly[T] { + pOut := e.NewPoly() + e.BinaryFourierMulAssign(p0, bfp, pOut) + return pOut +} + +// BinaryFourierMulAssign computes pOut = p0 * bfp, under the assumption that bfp is a binary polynomial. +// This is faster than [*Evaluator.MulAssign], and the result is exact unlike [*Evaluator.FourierMulAssign]. +func (e *Evaluator[T]) BinaryFourierMulAssign(p0 Poly[T], bfp FourierPoly, pOut Poly[T]) { + if e.splitCountBinary == 1 { + e.ToFourierPolyAssign(p0, e.buffer.fp0Split[0]) + e.MulFourierAssign(e.buffer.fp0Split[0], bfp, e.buffer.fpOutSplit[0]) + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + return + } + + if num.IsSigned[T]() { + var splitChunk T = 1 << e.splitBitsBinary + for i := 0; i < e.splitCountBinary; i++ { + var splitLow T = 1 << (i * int(e.splitBitsBinary)) + for j := 0; j < e.degree; j++ { + e.buffer.p0Split[i].Coeffs[j] = (p0.Coeffs[j] / splitLow) % splitChunk + } + } + } else { + var splitMask T = 1<> splitLowBits) & splitMask + } + } + } + + for i := 0; i < e.splitCountBinary; i++ { + e.ToFourierPolyAssign(e.buffer.p0Split[i], e.buffer.fp0Split[i]) + e.MulFourierAssign(e.buffer.fp0Split[i], bfp, e.buffer.fpOutSplit[i]) + } + + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + for i := 1; i < e.splitCountBinary; i++ { + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[i], e.buffer.p0Split[i]) + splitLowBits := i * int(e.splitBitsBinary) + for j := 0; j < e.degree; j++ { + pOut.Coeffs[j] += e.buffer.p0Split[i].Coeffs[j] << splitLowBits + } + } +} + +// BinaryFourierMulAddAssign computes pOut += p0 * bfp, under the assumption that bfp is a binary polynomial. +// This is faster than [*Evaluator.MulAddAssign], and the result is exact unlike [*Evaluator.FourierMulAddAssign]. +func (e *Evaluator[T]) BinaryFourierMulAddAssign(p0 Poly[T], bfp FourierPoly, pOut Poly[T]) { + if e.splitCountBinary == 1 { + e.ToFourierPolyAssign(p0, e.buffer.fp0Split[0]) + e.MulFourierAssign(e.buffer.fp0Split[0], bfp, e.buffer.fpOutSplit[0]) + e.ToPolyAddAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + return + } + + if num.IsSigned[T]() { + var splitChunk T = 1 << e.splitBitsBinary + for i := 0; i < e.splitCountBinary; i++ { + var splitLow T = 1 << (i * int(e.splitBitsBinary)) + for j := 0; j < e.degree; j++ { + e.buffer.p0Split[i].Coeffs[j] = (p0.Coeffs[j] / splitLow) % splitChunk + } + } + } else { + var splitMask T = 1<> splitLowBits) & splitMask + } + } + } + + for i := 0; i < e.splitCountBinary; i++ { + e.ToFourierPolyAssign(e.buffer.p0Split[i], e.buffer.fp0Split[i]) + e.MulFourierAssign(e.buffer.fp0Split[i], bfp, e.buffer.fpOutSplit[i]) + } + + e.ToPolyAddAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + for i := 1; i < e.splitCountBinary; i++ { + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[i], e.buffer.p0Split[i]) + splitLowBits := i * int(e.splitBitsBinary) + for j := 0; j < e.degree; j++ { + pOut.Coeffs[j] += e.buffer.p0Split[i].Coeffs[j] << splitLowBits + } + } +} + +// BinaryFourierMulSubAssign computes pOut -= p0 * bfp, under the assumption that bfp is a binary polynomial. +// This is faster than [*Evaluator.MulSubAssign], and the result is exact unlike [*Evaluator.FourierMulSubAssign]. +func (e *Evaluator[T]) BinaryFourierMulSubAssign(p0 Poly[T], bfp FourierPoly, pOut Poly[T]) { + if e.splitCountBinary == 1 { + e.ToFourierPolyAssign(p0, e.buffer.fp0Split[0]) + e.MulFourierAssign(e.buffer.fp0Split[0], bfp, e.buffer.fpOutSplit[0]) + e.ToPolySubAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + return + } + + if num.IsSigned[T]() { + var splitChunk T = 1 << e.splitBitsBinary + for i := 0; i < e.splitCountBinary; i++ { + var splitLow T = 1 << (i * int(e.splitBitsBinary)) + for j := 0; j < e.degree; j++ { + e.buffer.p0Split[i].Coeffs[j] = (p0.Coeffs[j] / splitLow) % splitChunk + } + } + } else { + var splitMask T = 1<> splitLowBits) & splitMask + } + } + } + + for i := 0; i < e.splitCountBinary; i++ { + e.ToFourierPolyAssign(e.buffer.p0Split[i], e.buffer.fp0Split[i]) + e.MulFourierAssign(e.buffer.fp0Split[i], bfp, e.buffer.fpOutSplit[i]) + } + + e.ToPolySubAssignUnsafe(e.buffer.fpOutSplit[0], pOut) + for i := 1; i < e.splitCountBinary; i++ { + e.ToPolyAssignUnsafe(e.buffer.fpOutSplit[i], e.buffer.p0Split[i]) + splitLowBits := i * int(e.splitBitsBinary) + for j := 0; j < e.degree; j++ { + pOut.Coeffs[j] -= e.buffer.p0Split[i].Coeffs[j] << splitLowBits + } + } +} diff --git a/math/poly/poly_ops.go b/math/poly/poly_ops.go index 70aa51e..f3c45fa 100644 --- a/math/poly/poly_ops.go +++ b/math/poly/poly_ops.go @@ -62,36 +62,32 @@ func (e *Evaluator[T]) ScalarMulSubAssign(p0 Poly[T], c T, pOut Poly[T]) { vec.ScalarMulSubAssign(p0.Coeffs, c, pOut.Coeffs) } -// Mul returns p0 * p1. -func (e *Evaluator[T]) Mul(p0, p1 Poly[T]) Poly[T] { - if e.degree <= karatsubaRecurseThreshold { - return e.mulNaive(p0, p1) - } - +// FourierMul returns p0 * fp. +func (e *Evaluator[T]) FourierMul(p0 Poly[T], fp FourierPoly) Poly[T] { pOut := e.NewPoly() - e.mulKaratsubaAssign(p0, p1, pOut) + e.FourierMulAssign(p0, fp, pOut) return pOut } -// MulAssign computes pOut = p0 * p1. -func (e *Evaluator[T]) MulAssign(p0, p1, pOut Poly[T]) { - if e.degree <= karatsubaRecurseThreshold { - e.mulNaiveAssign(p0, p1, pOut) - } else { - e.mulKaratsubaAssign(p0, p1, pOut) - } +// FourierMulAssign computes pOut = p0 * fp. +func (e *Evaluator[T]) FourierMulAssign(p0 Poly[T], fp FourierPoly, pOut Poly[T]) { + e.ToFourierPolyAssign(p0, e.buffer.fpMul) + e.MulFourierAssign(e.buffer.fpMul, fp, e.buffer.fpMul) + e.ToPolyAssignUnsafe(e.buffer.fpMul, pOut) } -// MulAddAssign computes pOut += p0 * p1. -func (e *Evaluator[T]) MulAddAssign(p0, p1, pOut Poly[T]) { - e.MulAssign(p0, p1, e.buffer.pOut) - e.AddAssign(pOut, e.buffer.pOut, pOut) +// FourierMulAddAssign computes pOut += p0 * fp. +func (e *Evaluator[T]) FourierMulAddAssign(p0 Poly[T], fp FourierPoly, pOut Poly[T]) { + e.ToFourierPolyAssign(p0, e.buffer.fpMul) + e.MulFourierAssign(e.buffer.fpMul, fp, e.buffer.fpMul) + e.ToPolyAddAssignUnsafe(e.buffer.fpMul, pOut) } -// MulSubAssign computes pOut -= p0 * p1. -func (e *Evaluator[T]) MulSubAssign(p0, p1, pOut Poly[T]) { - e.MulAssign(p0, p1, e.buffer.pOut) - e.SubAssign(pOut, e.buffer.pOut, pOut) +// FourierMulSubAssign computes pOut -= p0 * fp. +func (e *Evaluator[T]) FourierMulSubAssign(p0 Poly[T], fp FourierPoly, pOut Poly[T]) { + e.ToFourierPolyAssign(p0, e.buffer.fpMul) + e.MulFourierAssign(e.buffer.fpMul, fp, e.buffer.fpMul) + e.ToPolySubAssignUnsafe(e.buffer.fpMul, pOut) } // MonomialMul returns X^d * p0. diff --git a/math/poly/poly_test.go b/math/poly/poly_test.go index d6a0c36..fbc56f8 100644 --- a/math/poly/poly_test.go +++ b/math/poly/poly_test.go @@ -13,36 +13,71 @@ var ( NLog = []int{9, 10, 11, 12, 13, 14, 15} ) +func BenchmarkOps(b *testing.B) { + for _, nLog := range NLog { + N := 1 << nLog + + pev := poly.NewEvaluator[uint64](N) + + p0 := pev.NewPoly() + p1 := pev.NewPoly() + pOut := pev.NewPoly() + + for i := 0; i < pev.Degree(); i++ { + p0.Coeffs[i] = rand.Uint64() + p1.Coeffs[i] = rand.Uint64() + } + + b.Run(fmt.Sprintf("op=N=%v/Add", N), func(b *testing.B) { + for i := 0; i < b.N; i++ { + pev.AddAssign(p0, p1, pOut) + } + }) + + b.Run(fmt.Sprintf("op=N=%v/Sub", N), func(b *testing.B) { + for i := 0; i < b.N; i++ { + pev.SubAssign(p0, p1, pOut) + } + }) + + b.Run(fmt.Sprintf("op=N=%v/Mul", N), func(b *testing.B) { + for i := 0; i < b.N; i++ { + pev.MulAssign(p0, p1, pOut) + } + }) + } +} + func BenchmarkFourierOps(b *testing.B) { for _, nLog := range NLog { N := 1 << nLog - fft := poly.NewFourierEvaluator[uint64](N) + pev := poly.NewEvaluator[uint64](N) - fp0 := fft.NewFourierPoly() - fp1 := fft.NewFourierPoly() - fpOut := fft.NewFourierPoly() + fp0 := pev.NewFourierPoly() + fp1 := pev.NewFourierPoly() + fpOut := pev.NewFourierPoly() - for i := 0; i < fft.Degree(); i++ { + for i := 0; i < pev.Degree(); i++ { fp0.Coeffs[i] = (2*rand.Float64() - 1.0) * math.Exp(63) fp1.Coeffs[i] = (2*rand.Float64() - 1.0) * math.Exp(63) } - b.Run(fmt.Sprintf("op=Add/N=%v", N), func(b *testing.B) { + b.Run(fmt.Sprintf("N=%v/op=Add", N), func(b *testing.B) { for i := 0; i < b.N; i++ { - fft.AddAssign(fp0, fp1, fpOut) + pev.AddFourierAssign(fp0, fp1, fpOut) } }) - b.Run(fmt.Sprintf("op=Sub/N=%v", N), func(b *testing.B) { + b.Run(fmt.Sprintf("N=%v/op=Sub", N), func(b *testing.B) { for i := 0; i < b.N; i++ { - fft.SubAssign(fp0, fp1, fpOut) + pev.SubFourierAssign(fp0, fp1, fpOut) } }) - b.Run(fmt.Sprintf("op=Mul/N=%v", N), func(b *testing.B) { + b.Run(fmt.Sprintf("N=%v/op=Mul", N), func(b *testing.B) { for i := 0; i < b.N; i++ { - fft.MulAssign(fp0, fp1, fpOut) + pev.MulFourierAssign(fp0, fp1, fpOut) } }) } @@ -52,31 +87,31 @@ func BenchmarkFourierTransform(b *testing.B) { for _, nLog := range NLog { N := 1 << nLog - fft := poly.NewFourierEvaluator[uint64](N) + pev := poly.NewEvaluator[uint64](N) - p := fft.NewPoly() - fp := fft.NewFourierPoly() + p := pev.NewPoly() + fp := pev.NewFourierPoly() - for i := 0; i < fft.Degree(); i++ { + for i := 0; i < pev.Degree(); i++ { p.Coeffs[i] = rand.Uint64() } - b.Run(fmt.Sprintf("op=ToFourierPoly/N=%v", N), func(b *testing.B) { + b.Run(fmt.Sprintf("N=%v/op=ToFourierPoly", N), func(b *testing.B) { for i := 0; i < b.N; i++ { - fft.ToFourierPolyAssign(p, fp) + pev.ToFourierPolyAssign(p, fp) } }) x := N / 3 - b.Run(fmt.Sprintf("op=MonomialToFourierPoly/N=%v", N), func(b *testing.B) { + b.Run(fmt.Sprintf("N=%v/op=MonomialToFourierPoly", N), func(b *testing.B) { for i := 0; i < b.N; i++ { - fft.MonomialToFourierPolyAssign(x, fp) + pev.MonomialToFourierPolyAssign(x, fp) } }) - b.Run(fmt.Sprintf("op=ToPoly/N=%v", N), func(b *testing.B) { + b.Run(fmt.Sprintf("N=%v/op=ToPoly", N), func(b *testing.B) { for i := 0; i < b.N; i++ { - fft.ToPolyAssignUnsafe(fp, p) + pev.ToPolyAssignUnsafe(fp, p) } }) } diff --git a/mktfhe/bootstrap_keygen.go b/mktfhe/bootstrap_keygen.go index add6e59..44279a8 100644 --- a/mktfhe/bootstrap_keygen.go +++ b/mktfhe/bootstrap_keygen.go @@ -60,7 +60,7 @@ func (e *Encryptor[T]) GenCRSPublicKey() tfhe.FourierGLevCiphertext[T] { e.buffer.ctGLWESingle.Value[1].CopyFrom(e.CRS[i]) e.SingleKeyEncryptor.GaussianSampler.SampleSliceAssign(e.Parameters.GLWEStdDevQ(), e.buffer.ctGLWESingle.Value[0].Coeffs) - e.SingleKeyEncryptor.FourierEvaluator.PolyMulBinarySubAssign(e.SecretKey.FourierGLWEKey.Value[0], e.buffer.ctGLWESingle.Value[1], e.buffer.ctGLWESingle.Value[0]) + e.SingleKeyEncryptor.Evaluator.BinaryFourierMulSubAssign(e.buffer.ctGLWESingle.Value[1], e.SecretKey.FourierGLWEKey.Value[0], e.buffer.ctGLWESingle.Value[0]) e.SingleKeyEncryptor.ToFourierGLWECiphertextAssign(e.buffer.ctGLWESingle, pk.Value[i]) } diff --git a/mktfhe/fourier_glwe_conv.go b/mktfhe/fourier_glwe_conv.go index b6758cf..e7770cf 100644 --- a/mktfhe/fourier_glwe_conv.go +++ b/mktfhe/fourier_glwe_conv.go @@ -10,15 +10,15 @@ type GLWETransformer[T tfhe.TorusInt] struct { // Parameters holds parameters for this GLWETransformer. Parameters Parameters[T] - // FourierEvaluator is a FourierEvaluator for this GLWETransformer. - FourierEvaluator *poly.FourierEvaluator[T] + // PolyEvaluator is a PolyEvaluator for this GLWETransformer. + PolyEvaluator *poly.Evaluator[T] } // NewGLWETransformer allocates an empty GLWE transformer with given parameters. func NewGLWETransformer[T tfhe.TorusInt](params Parameters[T]) *GLWETransformer[T] { return &GLWETransformer[T]{ - Parameters: params, - FourierEvaluator: poly.NewFourierEvaluator[T](params.PolyDegree()), + Parameters: params, + PolyEvaluator: poly.NewEvaluator[T](params.PolyDegree()), } } @@ -26,8 +26,8 @@ func NewGLWETransformer[T tfhe.TorusInt](params Parameters[T]) *GLWETransformer[ // Returned GLWE transformer is safe for concurrent use. func (e *GLWETransformer[T]) ShallowCopy() *GLWETransformer[T] { return &GLWETransformer[T]{ - Parameters: e.Parameters, - FourierEvaluator: e.FourierEvaluator.ShallowCopy(), + Parameters: e.Parameters, + PolyEvaluator: e.PolyEvaluator.ShallowCopy(), } } @@ -41,7 +41,7 @@ func (e *GLWETransformer[T]) ToFourierGLWESecretKey(sk tfhe.GLWESecretKey[T]) tf // ToFourierGLWESecretKeyAssign transforms GLWE secret key to Fourier GLWE secret key and writes it to skOut. func (e *GLWETransformer[T]) ToFourierGLWESecretKeyAssign(skIn tfhe.GLWESecretKey[T], skOut tfhe.FourierGLWESecretKey[T]) { for i := 0; i < e.Parameters.SingleKeyGLWERank(); i++ { - e.FourierEvaluator.ToFourierPolyAssign(skIn.Value[i], skOut.Value[i]) + e.PolyEvaluator.ToFourierPolyAssign(skIn.Value[i], skOut.Value[i]) } } @@ -55,7 +55,7 @@ func (e *GLWETransformer[T]) ToGLWESecretKey(sk tfhe.FourierGLWESecretKey[T]) tf // ToGLWESecretKeyAssign transforms Fourier GLWE secret key to GLWE secret key and writes it to skOut. func (e *GLWETransformer[T]) ToGLWESecretKeyAssign(skIn tfhe.FourierGLWESecretKey[T], skOut tfhe.GLWESecretKey[T]) { for i := 0; i < e.Parameters.SingleKeyGLWERank(); i++ { - e.FourierEvaluator.ToPolyAssign(skIn.Value[i], skOut.Value[i]) + e.PolyEvaluator.ToPolyAssign(skIn.Value[i], skOut.Value[i]) } } @@ -69,7 +69,7 @@ func (e *GLWETransformer[T]) ToFourierGLWECiphertext(ct GLWECiphertext[T]) Fouri // ToFourierGLWECiphertextAssign transforms GLWE ciphertext to Fourier GLWE ciphertext and writes it to ctOut. func (e *GLWETransformer[T]) ToFourierGLWECiphertextAssign(ctIn GLWECiphertext[T], ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.ToFourierPolyAssign(ctIn.Value[i], ctOut.Value[i]) + e.PolyEvaluator.ToFourierPolyAssign(ctIn.Value[i], ctOut.Value[i]) } } @@ -83,7 +83,7 @@ func (e *GLWETransformer[T]) ToGLWECiphertext(ct FourierGLWECiphertext[T]) GLWEC // ToGLWECiphertextAssign transforms Fourier GLWE ciphertext to GLWE ciphertext and writes it to ctOut. func (e *GLWETransformer[T]) ToGLWECiphertextAssign(ctIn FourierGLWECiphertext[T], ctOut GLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.ToPolyAssign(ctIn.Value[i], ctOut.Value[i]) + e.PolyEvaluator.ToPolyAssign(ctIn.Value[i], ctOut.Value[i]) } } @@ -97,11 +97,11 @@ func (e *GLWETransformer[T]) ToFourierUniEncryption(ct UniEncryption[T]) Fourier // ToFourierUniEncryptionAssign transforms UniEncryption to Fourier UniEncryption and writes it to ctOut. func (e *GLWETransformer[T]) ToFourierUniEncryptionAssign(ctIn UniEncryption[T], ctOut FourierUniEncryption[T]) { for j := 0; j < ctIn.GadgetParameters.Level(); j++ { - e.FourierEvaluator.ToFourierPolyAssign(ctIn.Value[0].Value[j].Value[0], ctOut.Value[0].Value[j].Value[0]) - e.FourierEvaluator.ToFourierPolyAssign(ctIn.Value[0].Value[j].Value[1], ctOut.Value[0].Value[j].Value[1]) + e.PolyEvaluator.ToFourierPolyAssign(ctIn.Value[0].Value[j].Value[0], ctOut.Value[0].Value[j].Value[0]) + e.PolyEvaluator.ToFourierPolyAssign(ctIn.Value[0].Value[j].Value[1], ctOut.Value[0].Value[j].Value[1]) - e.FourierEvaluator.ToFourierPolyAssign(ctIn.Value[1].Value[j].Value[0], ctOut.Value[1].Value[j].Value[0]) - e.FourierEvaluator.ToFourierPolyAssign(ctIn.Value[1].Value[j].Value[1], ctOut.Value[1].Value[j].Value[1]) + e.PolyEvaluator.ToFourierPolyAssign(ctIn.Value[1].Value[j].Value[0], ctOut.Value[1].Value[j].Value[0]) + e.PolyEvaluator.ToFourierPolyAssign(ctIn.Value[1].Value[j].Value[1], ctOut.Value[1].Value[j].Value[1]) } } @@ -115,10 +115,10 @@ func (e *GLWETransformer[T]) ToUniEncryption(ct FourierUniEncryption[T]) UniEncr // ToUniEncryptionAssign transforms Fourier UniEncryption to UniEncryption and writes it to ctOut. func (e *GLWETransformer[T]) ToUniEncryptionAssign(ctIn FourierUniEncryption[T], ctOut UniEncryption[T]) { for j := 0; j < ctIn.GadgetParameters.Level(); j++ { - e.FourierEvaluator.ToPolyAssign(ctIn.Value[0].Value[j].Value[0], ctOut.Value[0].Value[j].Value[0]) - e.FourierEvaluator.ToPolyAssign(ctIn.Value[0].Value[j].Value[1], ctOut.Value[0].Value[j].Value[1]) + e.PolyEvaluator.ToPolyAssign(ctIn.Value[0].Value[j].Value[0], ctOut.Value[0].Value[j].Value[0]) + e.PolyEvaluator.ToPolyAssign(ctIn.Value[0].Value[j].Value[1], ctOut.Value[0].Value[j].Value[1]) - e.FourierEvaluator.ToPolyAssign(ctIn.Value[1].Value[j].Value[0], ctOut.Value[1].Value[j].Value[0]) - e.FourierEvaluator.ToPolyAssign(ctIn.Value[1].Value[j].Value[1], ctOut.Value[1].Value[j].Value[1]) + e.PolyEvaluator.ToPolyAssign(ctIn.Value[1].Value[j].Value[0], ctOut.Value[1].Value[j].Value[0]) + e.PolyEvaluator.ToPolyAssign(ctIn.Value[1].Value[j].Value[1], ctOut.Value[1].Value[j].Value[1]) } } diff --git a/mktfhe/fourier_glwe_enc.go b/mktfhe/fourier_glwe_enc.go index d108870..343e2fd 100644 --- a/mktfhe/fourier_glwe_enc.go +++ b/mktfhe/fourier_glwe_enc.go @@ -40,7 +40,7 @@ func (e *Encryptor[T]) FourierUniEncryptPlaintextAssign(pt tfhe.GLWEPlaintext[T] e.buffer.ctGLWESingle.Value[1].CopyFrom(e.CRS[i]) e.SingleKeyEncryptor.PolyEvaluator.ScalarMulAssign(pt.Value, ctOut.GadgetParameters.BaseQ(i), e.buffer.ctGLWESingle.Value[0]) - e.SingleKeyEncryptor.FourierEvaluator.PolyMulBinaryAddAssign(e.buffer.auxFourierKey.Value[0], e.buffer.ctGLWESingle.Value[1], e.buffer.ctGLWESingle.Value[0]) + e.SingleKeyEncryptor.Evaluator.BinaryFourierMulAddAssign(e.buffer.ctGLWESingle.Value[1], e.buffer.auxFourierKey.Value[0], e.buffer.ctGLWESingle.Value[0]) e.SingleKeyEncryptor.GaussianSampler.SampleSliceAddAssign(e.Parameters.GLWEStdDevQ(), e.buffer.ctGLWESingle.Value[0].Coeffs) e.SingleKeyEncryptor.ToFourierGLWECiphertextAssign(e.buffer.ctGLWESingle, ctOut.Value[0].Value[i]) @@ -87,5 +87,5 @@ func (e *Encryptor[T]) FourierUniDecryptPlaintextAssign(ct FourierUniEncryption[ e.SingleKeyEncryptor.ToGLWECiphertextAssign(ct.Value[0].Value[ct.GadgetParameters.Level()-1], e.buffer.ctGLWESingle) ptOut.Value.CopyFrom(e.buffer.ctGLWESingle.Value[0]) - e.SingleKeyEncryptor.FourierEvaluator.PolyMulBinarySubAssign(e.buffer.auxFourierKey.Value[0], e.buffer.ctGLWESingle.Value[1], ptOut.Value) + e.SingleKeyEncryptor.Evaluator.BinaryFourierMulSubAssign(e.buffer.ctGLWESingle.Value[1], e.buffer.auxFourierKey.Value[0], ptOut.Value) } diff --git a/mktfhe/fourier_glwe_ops.go b/mktfhe/fourier_glwe_ops.go index 3591a7f..4d089d0 100644 --- a/mktfhe/fourier_glwe_ops.go +++ b/mktfhe/fourier_glwe_ops.go @@ -14,7 +14,7 @@ func (e *Evaluator[T]) AddFourierGLWE(ct0, ct1 FourierGLWECiphertext[T]) Fourier // AddFourierGLWEAssign computes ctOut = ct0 + ct1. func (e *Evaluator[T]) AddFourierGLWEAssign(ct0, ct1, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.AddAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) + e.PolyEvaluator.AddFourierAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) } } @@ -28,7 +28,7 @@ func (e *Evaluator[T]) SubFourierGLWE(ct0, ct1 FourierGLWECiphertext[T]) Fourier // SubFourierGLWEAssign computes ctOut = ct0 - ct1. func (e *Evaluator[T]) SubFourierGLWEAssign(ct0, ct1, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.SubAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) + e.PolyEvaluator.SubFourierAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) } } @@ -42,7 +42,7 @@ func (e *Evaluator[T]) NegFourierGLWE(ct0 FourierGLWECiphertext[T]) FourierGLWEC // NegFourierGLWEAssign computes ctOut = -ct0. func (e *Evaluator[T]) NegFourierGLWEAssign(ct0, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.NegAssign(ct0.Value[i], ctOut.Value[i]) + e.PolyEvaluator.NegFourierAssign(ct0.Value[i], ctOut.Value[i]) } } @@ -56,21 +56,21 @@ func (e *Evaluator[T]) FloatMulFourierGLWE(ct0 FourierGLWECiphertext[T], c float // FloatMulFourierGLWEAssign computes ctOut = c * ct0. func (e *Evaluator[T]) FloatMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c float64, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.FloatMulAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.FloatMulFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // FloatMulAddFourierGLWEAssign computes ctOut += c * ct0. func (e *Evaluator[T]) FloatMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c float64, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.FloatMulAddAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.FloatMulAddFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // FloatMulSubFourierGLWEAssign computes ctOut -= c * ct0. func (e *Evaluator[T]) FloatMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c float64, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.FloatMulSubAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.FloatMulSubFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } @@ -84,21 +84,21 @@ func (e *Evaluator[T]) CmplxMulFourierGLWE(ct0 FourierGLWECiphertext[T], c compl // CmplxMulFourierGLWEAssign computes ctOut = c * ct0. func (e *Evaluator[T]) CmplxMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c complex128, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.CmplxMulAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.CmplxMulFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // CmplxMulAddFourierGLWEAssign computes ctOut += c * ct0. func (e *Evaluator[T]) CmplxMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c complex128, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.CmplxMulAddAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.CmplxMulAddFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // CmplxMulSubFourierGLWEAssign computes ctOut -= c * ct0. func (e *Evaluator[T]) CmplxMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c complex128, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.CmplxMulSubAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.CmplxMulSubFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } @@ -112,21 +112,21 @@ func (e *Evaluator[T]) PolyMulFourierGLWE(ct0 FourierGLWECiphertext[T], p poly.P // PolyMulFourierGLWEAssign computes ctOut = p * ct0. func (e *Evaluator[T]) PolyMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], p poly.Poly[T], ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.PolyMulAssign(ct0.Value[i], p, ctOut.Value[i]) + e.PolyEvaluator.PolyMulFourierAssign(ct0.Value[i], p, ctOut.Value[i]) } } // PolyMulAddFourierGLWEAssign computes ctOut += p * ct0. func (e *Evaluator[T]) PolyMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], p poly.Poly[T], ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.PolyMulAddAssign(ct0.Value[i], p, ctOut.Value[i]) + e.PolyEvaluator.PolyMulAddFourierAssign(ct0.Value[i], p, ctOut.Value[i]) } } // PolyMulSubFourierGLWEAssign computes ctOut -= p * ct0. func (e *Evaluator[T]) PolyMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], p poly.Poly[T], ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.PolyMulSubAssign(ct0.Value[i], p, ctOut.Value[i]) + e.PolyEvaluator.PolyMulSubFourierAssign(ct0.Value[i], p, ctOut.Value[i]) } } @@ -140,20 +140,20 @@ func (e *Evaluator[T]) FourierPolyMulFourierGLWE(ct0 FourierGLWECiphertext[T], f // FourierPolyMulFourierGLWEAssign computes ctOut = fp * ct0. func (e *Evaluator[T]) FourierPolyMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], fp poly.FourierPoly, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.MulAssign(ct0.Value[i], fp, ctOut.Value[i]) + e.PolyEvaluator.MulFourierAssign(ct0.Value[i], fp, ctOut.Value[i]) } } // FourierPolyMulAddFourierGLWEAssign computes ctOut += fp * ct0. func (e *Evaluator[T]) FourierPolyMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], fp poly.FourierPoly, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.MulAddAssign(ct0.Value[i], fp, ctOut.Value[i]) + e.PolyEvaluator.MulAddFourierAssign(ct0.Value[i], fp, ctOut.Value[i]) } } // FourierPolyMulSubFourierGLWEAssign computes ctOut -= fp * ct0. func (e *Evaluator[T]) FourierPolyMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], fp poly.FourierPoly, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.GLWERank()+1; i++ { - e.FourierEvaluator.MulSubAssign(ct0.Value[i], fp, ctOut.Value[i]) + e.PolyEvaluator.MulSubFourierAssign(ct0.Value[i], fp, ctOut.Value[i]) } } diff --git a/mktfhe/glwe_enc.go b/mktfhe/glwe_enc.go index b4f579a..a59f686 100644 --- a/mktfhe/glwe_enc.go +++ b/mktfhe/glwe_enc.go @@ -40,7 +40,7 @@ func (e *Encryptor[T]) UniEncryptPlaintextAssign(pt tfhe.GLWEPlaintext[T], ctOut ctOut.Value[0].Value[i].Value[1].CopyFrom(e.CRS[i]) e.SingleKeyEncryptor.PolyEvaluator.ScalarMulAssign(pt.Value, ctOut.GadgetParameters.BaseQ(i), ctOut.Value[0].Value[i].Value[0]) - e.SingleKeyEncryptor.FourierEvaluator.PolyMulBinaryAddAssign(e.buffer.auxFourierKey.Value[0], ctOut.Value[0].Value[i].Value[1], ctOut.Value[0].Value[i].Value[0]) + e.SingleKeyEncryptor.Evaluator.BinaryFourierMulAddAssign(ctOut.Value[0].Value[i].Value[1], e.buffer.auxFourierKey.Value[0], ctOut.Value[0].Value[i].Value[0]) e.SingleKeyEncryptor.GaussianSampler.SampleSliceAddAssign(e.Parameters.GLWEStdDevQ(), ctOut.Value[0].Value[i].Value[0].Coeffs) } @@ -83,5 +83,5 @@ func (e *Encryptor[T]) UniDecryptPlaintextAssign(ct UniEncryption[T], ptOut tfhe e.SingleKeyEncryptor.ToFourierGLWESecretKeyAssign(e.buffer.auxKey, e.buffer.auxFourierKey) ptOut.Value.CopyFrom(ct.Value[0].Value[ct.GadgetParameters.Level()-1].Value[0]) - e.SingleKeyEncryptor.FourierEvaluator.PolyMulBinarySubAssign(e.buffer.auxFourierKey.Value[0], ct.Value[0].Value[ct.GadgetParameters.Level()-1].Value[1], ptOut.Value) + e.SingleKeyEncryptor.Evaluator.BinaryFourierMulSubAssign(ct.Value[0].Value[ct.GadgetParameters.Level()-1].Value[1], e.buffer.auxFourierKey.Value[0], ptOut.Value) } diff --git a/mktfhe/glwe_ops.go b/mktfhe/glwe_ops.go index fbc4291..f434b1c 100644 --- a/mktfhe/glwe_ops.go +++ b/mktfhe/glwe_ops.go @@ -133,6 +133,34 @@ func (e *Evaluator[T]) PolyMulSubGLWEAssign(ct0 GLWECiphertext[T], p poly.Poly[T } } +// FourierPolyMulGLWE returns fp * ct0. +func (e *Evaluator[T]) FourierPolyMulGLWE(ct0 GLWECiphertext[T], fp poly.FourierPoly) GLWECiphertext[T] { + ctOut := NewGLWECiphertext(e.Parameters) + e.FourierPolyMulGLWEAssign(ct0, fp, ctOut) + return ctOut +} + +// FourierPolyMulGLWEAssign computes ctOut = fp * ct0. +func (e *Evaluator[T]) FourierPolyMulGLWEAssign(ct0 GLWECiphertext[T], fp poly.FourierPoly, ctOut GLWECiphertext[T]) { + for i := 0; i < e.Parameters.GLWERank()+1; i++ { + e.PolyEvaluator.FourierMulAssign(ct0.Value[i], fp, ctOut.Value[i]) + } +} + +// FourierPolyMulAddGLWEAssign computes ctOut += fp * ct0. +func (e *Evaluator[T]) FourierPolyMulAddGLWEAssign(ct0 GLWECiphertext[T], fp poly.FourierPoly, ctOut GLWECiphertext[T]) { + for i := 0; i < e.Parameters.GLWERank()+1; i++ { + e.PolyEvaluator.FourierMulAddAssign(ct0.Value[i], fp, ctOut.Value[i]) + } +} + +// FourierPolyMulSubGLWEAssign computes ctOut -= fp * ct0. +func (e *Evaluator[T]) FourierPolyMulSubGLWEAssign(ct0 GLWECiphertext[T], fp poly.FourierPoly, ctOut GLWECiphertext[T]) { + for i := 0; i < e.Parameters.GLWERank()+1; i++ { + e.PolyEvaluator.FourierMulSubAssign(ct0.Value[i], fp, ctOut.Value[i]) + } +} + // MonomialMulGLWE returns X^d * ct0. func (e *Evaluator[T]) MonomialMulGLWE(ct0 GLWECiphertext[T], d int) GLWECiphertext[T] { ctOut := NewGLWECiphertext(e.Parameters) diff --git a/mktfhe/product.go b/mktfhe/product.go index c46f6f0..346a2ab 100644 --- a/mktfhe/product.go +++ b/mktfhe/product.go @@ -20,50 +20,50 @@ func (e *Evaluator[T]) HybridProductGLWEAssign(idx int, ctFourierUniEnc FourierU e.DecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } - eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulFourierAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0]) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) } - eIdx.FourierEvaluator.MulAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[0].Value[1], polyFourierDecomposed[0], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulFourierAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[0].Value[1], polyFourierDecomposed[0], e.buffer.ctFourierProdSingle) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulAddFourierAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) } - eIdx.FourierEvaluator.NegAssign(e.buffer.ctFourierProdSingle, e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.NegFourierAssign(e.buffer.ctFourierProdSingle, e.buffer.ctFourierProdSingle) for i, ok := range e.PartyBitMap { if ok { e.DecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } - eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1]) + eIdx.PolyEvaluator.MulFourierAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1]) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[i+1]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[i+1]) } for j := 0; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(e.EvaluationKeys[i].CRSPublicKey.Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulAddFourierAssign(e.EvaluationKeys[i].CRSPublicKey.Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) } } } - eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle) + eIdx.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle) e.DecomposePolyAssign(e.buffer.ctProdSingle, ctFourierUniEnc.GadgetParameters, polyDecomposed) for j := 0; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[j], polyFourierDecomposed[j]) - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[1].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[1].Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[1+idx]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[j], polyFourierDecomposed[j]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[1].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[1].Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[1+idx]) } - eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[0], ctGLWEOut.Value[0]) + eIdx.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[0], ctGLWEOut.Value[0]) for i, ok := range e.PartyBitMap { if ok { - eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[i+1], ctGLWEOut.Value[i+1]) + eIdx.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[i+1], ctGLWEOut.Value[i+1]) } else { ctGLWEOut.Value[i+1].Clear() } @@ -79,50 +79,50 @@ func (e *Evaluator[T]) HybridProductAddGLWEAssign(idx int, ctFourierUniEnc Fouri e.DecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } - eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulFourierAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0]) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) } - eIdx.FourierEvaluator.MulAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[0].Value[1], polyFourierDecomposed[0], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulFourierAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[0].Value[1], polyFourierDecomposed[0], e.buffer.ctFourierProdSingle) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulAddFourierAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) } - eIdx.FourierEvaluator.NegAssign(e.buffer.ctFourierProdSingle, e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.NegFourierAssign(e.buffer.ctFourierProdSingle, e.buffer.ctFourierProdSingle) for i, ok := range e.PartyBitMap { if ok { e.DecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } - eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1]) + eIdx.PolyEvaluator.MulFourierAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1]) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[i+1]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[i+1]) } for j := 0; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(e.EvaluationKeys[i].CRSPublicKey.Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulAddFourierAssign(e.EvaluationKeys[i].CRSPublicKey.Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) } } } - eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle) + eIdx.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle) e.DecomposePolyAssign(e.buffer.ctProdSingle, ctFourierUniEnc.GadgetParameters, polyDecomposed) for j := 0; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[j], polyFourierDecomposed[j]) - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[1].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[1].Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[1+idx]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[j], polyFourierDecomposed[j]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[1].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[1].Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[1+idx]) } - eIdx.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[0], ctGLWEOut.Value[0]) + eIdx.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[0], ctGLWEOut.Value[0]) for i, ok := range e.PartyBitMap { if ok { - eIdx.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[i+1], ctGLWEOut.Value[i+1]) + eIdx.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[i+1], ctGLWEOut.Value[i+1]) } } } @@ -136,50 +136,50 @@ func (e *Evaluator[T]) HybridProductSubGLWEAssign(idx int, ctFourierUniEnc Fouri e.DecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } - eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulFourierAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0]) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) } - eIdx.FourierEvaluator.MulAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[0].Value[1], polyFourierDecomposed[0], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulFourierAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[0].Value[1], polyFourierDecomposed[0], e.buffer.ctFourierProdSingle) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulAddFourierAssign(e.EvaluationKeys[idx].CRSPublicKey.Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) } - eIdx.FourierEvaluator.NegAssign(e.buffer.ctFourierProdSingle, e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.NegFourierAssign(e.buffer.ctFourierProdSingle, e.buffer.ctFourierProdSingle) for i, ok := range e.PartyBitMap { if ok { e.DecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } - eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1]) + eIdx.PolyEvaluator.MulFourierAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1]) for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[i+1]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[0].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[i+1]) } for j := 0; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - eIdx.FourierEvaluator.MulAddAssign(e.EvaluationKeys[i].CRSPublicKey.Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) + eIdx.PolyEvaluator.MulAddFourierAssign(e.EvaluationKeys[i].CRSPublicKey.Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProdSingle) } } } - eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle) + eIdx.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle) e.DecomposePolyAssign(e.buffer.ctProdSingle, ctFourierUniEnc.GadgetParameters, polyDecomposed) for j := 0; j < ctFourierUniEnc.GadgetParameters.Level(); j++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[j], polyFourierDecomposed[j]) - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[1].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) - eIdx.FourierEvaluator.MulAddAssign(ctFourierUniEnc.Value[1].Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[1+idx]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[j], polyFourierDecomposed[j]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[1].Value[j].Value[0], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[0]) + eIdx.PolyEvaluator.MulAddFourierAssign(ctFourierUniEnc.Value[1].Value[j].Value[1], polyFourierDecomposed[j], e.buffer.ctFourierProd.Value[1+idx]) } - eIdx.FourierEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[0], ctGLWEOut.Value[0]) + eIdx.PolyEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[0], ctGLWEOut.Value[0]) for i, ok := range e.PartyBitMap { if ok { - eIdx.FourierEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[i+1], ctGLWEOut.Value[i+1]) + eIdx.PolyEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[i+1], ctGLWEOut.Value[i+1]) } } } diff --git a/tfhe/bootstrap.go b/tfhe/bootstrap.go index 37aa597..4a3946d 100644 --- a/tfhe/bootstrap.go +++ b/tfhe/bootstrap.go @@ -175,7 +175,7 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp for j := 0; j < e.Parameters.polyExtendFactor; j++ { e.DecomposePolyAssign(e.buffer.ctAcc[j].Value[0], e.Parameters.bootstrapParameters, polyDecomposed) for l := 0; l < e.Parameters.bootstrapParameters.level; l++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[l], e.buffer.ctAccFourierDecomposed[j][0][l]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[l], e.buffer.ctAccFourierDecomposed[j][0][l]) } } @@ -187,17 +187,17 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp } if a2NIdx == 0 { - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k := 0; k < e.Parameters.polyExtendFactor; k++ { e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[k], e.buffer.fMono, e.buffer.ctFourierAcc[k]) } } else { - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) for k, kk := 0, e.Parameters.polyExtendFactor-a2NIdx; k < a2NIdx; k, kk = k+1, kk+1 { e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) } - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k, kk := a2NIdx, 0; k < e.Parameters.polyExtendFactor; k, kk = k+1, kk+1 { e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) @@ -213,17 +213,17 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp } if a2NIdx == 0 { - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k := 0; k < e.Parameters.polyExtendFactor; k++ { e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[k], e.buffer.fMono, e.buffer.ctFourierAcc[k]) } } else { - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) for k, kk := 0, e.Parameters.polyExtendFactor-a2NIdx; k < a2NIdx; k, kk = k+1, kk+1 { e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) } - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k, kk := a2NIdx, 0; k < e.Parameters.polyExtendFactor; k, kk = k+1, kk+1 { e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) @@ -233,7 +233,7 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp for j := 0; j < e.Parameters.polyExtendFactor; j++ { for k := 0; k < e.Parameters.glweRank+1; k++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[j].Value[k], e.buffer.ctAcc[j].Value[k]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[j].Value[k], e.buffer.ctAcc[j].Value[k]) } } @@ -242,7 +242,7 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp for k := 0; k < e.Parameters.glweRank+1; k++ { e.DecomposePolyAssign(e.buffer.ctAcc[j].Value[k], e.Parameters.bootstrapParameters, polyDecomposed) for l := 0; l < e.Parameters.bootstrapParameters.level; l++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[l], e.buffer.ctAccFourierDecomposed[j][k][l]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[l], e.buffer.ctAccFourierDecomposed[j][k][l]) } } } @@ -255,17 +255,17 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp } if a2NIdx == 0 { - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k := 0; k < e.Parameters.polyExtendFactor; k++ { e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[k], e.buffer.fMono, e.buffer.ctFourierAcc[k]) } } else { - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) for k, kk := 0, e.Parameters.polyExtendFactor-a2NIdx; k < a2NIdx; k, kk = k+1, kk+1 { e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) } - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k, kk := a2NIdx, 0; k < e.Parameters.polyExtendFactor; k, kk = k+1, kk+1 { e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) @@ -281,17 +281,17 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp } if a2NIdx == 0 { - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k := 0; k < e.Parameters.polyExtendFactor; k++ { e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[k], e.buffer.fMono, e.buffer.ctFourierAcc[k]) } } else { - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) for k, kk := 0, e.Parameters.polyExtendFactor-a2NIdx; k < a2NIdx; k, kk = k+1, kk+1 { e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) } - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall, e.buffer.fMono) for k, kk := a2NIdx, 0; k < e.Parameters.polyExtendFactor; k, kk = k+1, kk+1 { e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[k]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[k], e.buffer.ctBlockFourierAcc[k], e.buffer.ctFourierAcc[k]) @@ -301,7 +301,7 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp for j := 0; j < e.Parameters.polyExtendFactor; j++ { for k := 0; k < e.Parameters.glweRank+1; k++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[j].Value[k], e.buffer.ctAcc[j].Value[k]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[j].Value[k], e.buffer.ctAcc[j].Value[k]) } } } @@ -310,7 +310,7 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp for k := 0; k < e.Parameters.glweRank+1; k++ { e.DecomposePolyAssign(e.buffer.ctAcc[j].Value[k], e.Parameters.bootstrapParameters, polyDecomposed) for l := 0; l < e.Parameters.bootstrapParameters.level; l++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[l], e.buffer.ctAccFourierDecomposed[j][k][l]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[l], e.buffer.ctAccFourierDecomposed[j][k][l]) } } } @@ -320,13 +320,13 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp if a2NIdx == 0 { e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[e.Parameters.lweDimension-e.Parameters.blockSize], e.buffer.ctAccFourierDecomposed[0], e.buffer.ctBlockFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) } else { kk := e.Parameters.polyExtendFactor - a2NIdx e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[e.Parameters.lweDimension-e.Parameters.blockSize], e.buffer.ctAccFourierDecomposed[0], e.buffer.ctBlockFourierAcc[0]) e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[e.Parameters.lweDimension-e.Parameters.blockSize], e.buffer.ctAccFourierDecomposed[kk], e.buffer.ctBlockFourierAcc[kk]) - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[0]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[0], e.buffer.ctBlockFourierAcc[0], e.buffer.ctFourierAcc[0]) } @@ -337,20 +337,20 @@ func (e *Evaluator[T]) blindRotateExtendedAssign(ct LWECiphertext[T], lut LookUp if a2NIdx == 0 { e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[j], e.buffer.ctAccFourierDecomposed[0], e.buffer.ctBlockFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(a2NSmall, e.buffer.fMono) e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) } else { kk := e.Parameters.polyExtendFactor - a2NIdx e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[j], e.buffer.ctAccFourierDecomposed[0], e.buffer.ctBlockFourierAcc[0]) e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[j], e.buffer.ctAccFourierDecomposed[kk], e.buffer.ctBlockFourierAcc[kk]) - e.FourierEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) + e.PolyEvaluator.MonomialToFourierPolyAssign(a2NSmall+1, e.buffer.fMono) e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[kk], e.buffer.fMono, e.buffer.ctFourierAcc[0]) e.SubFourierGLWEAssign(e.buffer.ctFourierAcc[0], e.buffer.ctBlockFourierAcc[0], e.buffer.ctFourierAcc[0]) } } for k := 0; k < e.Parameters.glweRank+1; k++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[k], e.buffer.ctAcc[0].Value[k]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[k], e.buffer.ctAcc[0].Value[k]) ctOut.Value[k].CopyFrom(e.buffer.ctAcc[0].Value[k]) } } @@ -368,41 +368,41 @@ func (e *Evaluator[T]) blindRotateBlockAssign(ct LWECiphertext[T], lut LookUpTab e.DecomposePolyAssign(ctOut.Value[0], e.Parameters.bootstrapParameters, polyDecomposed) for k := 0; k < e.Parameters.bootstrapParameters.level; k++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][0][k]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][0][k]) } e.GadgetProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[0].Value[0], e.buffer.ctAccFourierDecomposed[0][0], e.buffer.ctBlockFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[1]), e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[1]), e.buffer.fMono) e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) for j := 1; j < e.Parameters.blockSize; j++ { e.GadgetProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[j].Value[0], e.buffer.ctAccFourierDecomposed[0][0], e.buffer.ctBlockFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[j+1]), e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[j+1]), e.buffer.fMono) e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) } for j := 0; j < e.Parameters.glweRank+1; j++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) } for i := 1; i < e.Parameters.blockCount; i++ { for j := 0; j < e.Parameters.glweRank+1; j++ { e.DecomposePolyAssign(ctOut.Value[j], e.Parameters.bootstrapParameters, polyDecomposed) for k := 0; k < e.Parameters.bootstrapParameters.level; k++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][j][k]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][j][k]) } } e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[i*e.Parameters.blockSize], e.buffer.ctAccFourierDecomposed[0], e.buffer.ctBlockFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[i*e.Parameters.blockSize+1]), e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[i*e.Parameters.blockSize+1]), e.buffer.fMono) e.FourierPolyMulFourierGLWEAssign(e.buffer.ctBlockFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) for j := i*e.Parameters.blockSize + 1; j < (i+1)*e.Parameters.blockSize; j++ { e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[j], e.buffer.ctAccFourierDecomposed[0], e.buffer.ctBlockFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[j+1]), e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[j+1]), e.buffer.fMono) e.FourierPolyMulAddFourierGLWEAssign(e.buffer.ctBlockFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) } for j := 0; j < e.Parameters.glweRank+1; j++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) } } } @@ -420,30 +420,30 @@ func (e *Evaluator[T]) blindRotateOriginalAssign(ct LWECiphertext[T], lut LookUp e.DecomposePolyAssign(ctOut.Value[0], e.Parameters.bootstrapParameters, polyDecomposed) for k := 0; k < e.Parameters.bootstrapParameters.level; k++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][0][k]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][0][k]) } e.GadgetProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[0].Value[0], e.buffer.ctAccFourierDecomposed[0][0], e.buffer.ctFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[1]), e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[1]), e.buffer.fMono) e.FourierPolyMulFourierGLWEAssign(e.buffer.ctFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) for j := 0; j < e.Parameters.glweRank+1; j++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) } for i := 1; i < e.Parameters.lweDimension; i++ { for j := 0; j < e.Parameters.glweRank+1; j++ { e.DecomposePolyAssign(ctOut.Value[j], e.Parameters.bootstrapParameters, polyDecomposed) for k := 0; k < e.Parameters.bootstrapParameters.level; k++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][j][k]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[k], e.buffer.ctAccFourierDecomposed[0][j][k]) } } e.ExternalProductHoistedFourierGLWEAssign(e.EvaluationKey.BootstrapKey.Value[i], e.buffer.ctAccFourierDecomposed[0], e.buffer.ctFourierAcc[0]) - e.FourierEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[i+1]), e.buffer.fMono) + e.PolyEvaluator.MonomialSubOneToFourierPolyAssign(-e.ModSwitch(ct.Value[i+1]), e.buffer.fMono) e.FourierPolyMulFourierGLWEAssign(e.buffer.ctFourierAcc[0], e.buffer.fMono, e.buffer.ctFourierAcc[0]) for j := 0; j < e.Parameters.glweRank+1; j++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierAcc[0].Value[j], ctOut.Value[j]) } } } diff --git a/tfhe/encryptor.go b/tfhe/encryptor.go index 7b22a5e..17ee7c6 100644 --- a/tfhe/encryptor.go +++ b/tfhe/encryptor.go @@ -29,8 +29,6 @@ type Encryptor[T TorusInt] struct { // PolyEvaluator holds the PolyEvaluator for this Encryptor. PolyEvaluator *poly.Evaluator[T] - // FourierEvaluator holds the FourierEvaluator for this Encryptor. - FourierEvaluator *poly.FourierEvaluator[T] // SecretKey holds the LWE and GLWE key for this Encryptor. // @@ -67,8 +65,7 @@ func NewEncryptor[T TorusInt](params Parameters[T]) *Encryptor[T] { BinarySampler: csprng.NewBinarySampler[T](), GaussianSampler: csprng.NewGaussianSampler[T](), - PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), - FourierEvaluator: poly.NewFourierEvaluator[T](params.polyDegree), + PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), buffer: newEncryptionBuffer(params), } @@ -91,8 +88,7 @@ func NewEncryptorWithKey[T TorusInt](params Parameters[T], sk SecretKey[T]) *Enc BinarySampler: csprng.NewBinarySampler[T](), GaussianSampler: csprng.NewGaussianSampler[T](), - PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), - FourierEvaluator: poly.NewFourierEvaluator[T](params.polyDegree), + PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), SecretKey: sk, @@ -124,8 +120,7 @@ func (e *Encryptor[T]) ShallowCopy() *Encryptor[T] { SecretKey: e.SecretKey, - PolyEvaluator: e.PolyEvaluator.ShallowCopy(), - FourierEvaluator: e.FourierEvaluator.ShallowCopy(), + PolyEvaluator: e.PolyEvaluator.ShallowCopy(), buffer: newEncryptionBuffer(e.Parameters), } @@ -183,7 +178,7 @@ func (e *Encryptor[T]) GenPublicKey() PublicKey[T] { e.GaussianSampler.SampleSliceAssign(e.Parameters.GLWEStdDevQ(), pk.LWEKey.Value[i].Value[0].Coeffs) for j := 1; j < e.Parameters.glweRank+1; j++ { e.UniformSampler.SampleSliceAssign(pk.LWEKey.Value[i].Value[j].Coeffs) - e.FourierEvaluator.PolyMulBinarySubAssign(fskRev.Value[j-1], pk.LWEKey.Value[i].Value[j], pk.LWEKey.Value[i].Value[0]) + e.Evaluator.BinaryFourierMulSubAssign(pk.LWEKey.Value[i].Value[j], fskRev.Value[j-1], pk.LWEKey.Value[i].Value[0]) } } diff --git a/tfhe/encryptor_public.go b/tfhe/encryptor_public.go index 4857f8f..06c9289 100644 --- a/tfhe/encryptor_public.go +++ b/tfhe/encryptor_public.go @@ -31,8 +31,6 @@ type PublicEncryptor[T TorusInt] struct { // PolyEvaluator holds the PolyEvaluator for this PublicEncryptor. PolyEvaluator *poly.Evaluator[T] - // FourierEvaluator holds the FourierEvaluator for this PublicEncryptor. - FourierEvaluator *poly.FourierEvaluator[T] // PublicKey holds the public key for this PublicEncryptor. PublicKey PublicKey[T] @@ -73,8 +71,7 @@ func NewPublicEncryptor[T TorusInt](params Parameters[T], pk PublicKey[T]) *Publ BinarySampler: csprng.NewBinarySampler[T](), GaussianSampler: csprng.NewGaussianSampler[T](), - PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), - FourierEvaluator: poly.NewFourierEvaluator[T](params.polyDegree), + PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), PublicKey: pk, @@ -105,8 +102,7 @@ func (e *PublicEncryptor[T]) ShallowCopy() *PublicEncryptor[T] { BinarySampler: csprng.NewBinarySampler[T](), GaussianSampler: csprng.NewGaussianSampler[T](), - PolyEvaluator: e.PolyEvaluator.ShallowCopy(), - FourierEvaluator: e.FourierEvaluator.ShallowCopy(), + PolyEvaluator: e.PolyEvaluator.ShallowCopy(), PublicKey: e.PublicKey, diff --git a/tfhe/evaluator.go b/tfhe/evaluator.go index 5a73727..d3b17b7 100644 --- a/tfhe/evaluator.go +++ b/tfhe/evaluator.go @@ -24,8 +24,6 @@ type Evaluator[T TorusInt] struct { // PolyEvaluator holds the PolyEvaluator for this Evaluator. PolyEvaluator *poly.Evaluator[T] - // FourierEvaluator holds the FourierEvaluator for this Evaluator. - FourierEvaluator *poly.FourierEvaluator[T] // EvaluationKey holds the evaluation key for this Evaluator. EvaluationKey EvaluationKey[T] @@ -52,8 +50,8 @@ type evaluationBuffer[T TorusInt] struct { // Use [*Evaluator.polyFourierDecomposedBuffer] to get appropriate length of buffer. polyFourierDecomposed []poly.FourierPoly - // fpOut holds the fourier transformed polynomial for multiplications. - fpOut poly.FourierPoly + // fpMul holds the fourier transformed polynomial for multiplications. + fpMul poly.FourierPoly // ctFourierProd holds the fourier transformed ctGLWEOut in ExternalProductFourier. ctFourierProd FourierGLWECiphertext[T] // ctCMux holds ct1 - ct0 in CMux. @@ -96,8 +94,7 @@ func NewEvaluator[T TorusInt](params Parameters[T], evk EvaluationKey[T]) *Evalu Parameters: params, - PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), - FourierEvaluator: poly.NewFourierEvaluator[T](params.polyDegree), + PolyEvaluator: poly.NewEvaluator[T](params.polyDegree), EvaluationKey: evk, @@ -141,7 +138,7 @@ func newEvaluationBuffer[T TorusInt](params Parameters[T]) evaluationBuffer[T] { polyDecomposed: polyDecomposed, polyFourierDecomposed: polyFourierDecomposed, - fpOut: poly.NewFourierPoly(params.polyDegree), + fpMul: poly.NewFourierPoly(params.polyDegree), ctFourierProd: NewFourierGLWECiphertext(params), ctCMux: NewGLWECiphertext(params), @@ -169,8 +166,7 @@ func (e *Evaluator[T]) ShallowCopy() *Evaluator[T] { Parameters: e.Parameters, - PolyEvaluator: e.PolyEvaluator.ShallowCopy(), - FourierEvaluator: e.FourierEvaluator.ShallowCopy(), + PolyEvaluator: e.PolyEvaluator.ShallowCopy(), EvaluationKey: e.EvaluationKey, @@ -220,7 +216,7 @@ func (e *Evaluator[T]) polyFourierDecomposedBuffer(gadgetParams GadgetParameters oldLen := len(e.buffer.polyFourierDecomposed) e.buffer.polyFourierDecomposed = append(e.buffer.polyFourierDecomposed, make([]poly.FourierPoly, gadgetParams.level-oldLen)...) for i := oldLen; i < gadgetParams.level; i++ { - e.buffer.polyFourierDecomposed[i] = e.FourierEvaluator.NewFourierPoly() + e.buffer.polyFourierDecomposed[i] = e.PolyEvaluator.NewFourierPoly() } return e.buffer.polyFourierDecomposed } diff --git a/tfhe/fourier_glwe_conv.go b/tfhe/fourier_glwe_conv.go index d5f3b4b..1345524 100644 --- a/tfhe/fourier_glwe_conv.go +++ b/tfhe/fourier_glwe_conv.go @@ -13,15 +13,15 @@ type GLWETransformer[T TorusInt] struct { // Parameters holds parameters for this GLWETransformer. Parameters Parameters[T] - // FourierEvaluator is a FourierEvaluator for this GLWETransformer. - FourierEvaluator *poly.FourierEvaluator[T] + // Evaluator is a Evaluator for this GLWETransformer. + Evaluator *poly.Evaluator[T] } // NewGLWETransformer returns a new GLWETransformer with given parameters. func NewGLWETransformer[T TorusInt](params Parameters[T]) *GLWETransformer[T] { return &GLWETransformer[T]{ - Parameters: params, - FourierEvaluator: poly.NewFourierEvaluator[T](params.polyDegree), + Parameters: params, + Evaluator: poly.NewEvaluator[T](params.polyDegree), } } @@ -29,8 +29,8 @@ func NewGLWETransformer[T TorusInt](params Parameters[T]) *GLWETransformer[T] { // Returned GLWETransformer is safe for concurrent use. func (e *GLWETransformer[T]) ShallowCopy() *GLWETransformer[T] { return &GLWETransformer[T]{ - Parameters: e.Parameters, - FourierEvaluator: e.FourierEvaluator.ShallowCopy(), + Parameters: e.Parameters, + Evaluator: e.Evaluator.ShallowCopy(), } } @@ -44,7 +44,7 @@ func (e *GLWETransformer[T]) ToFourierGLWESecretKey(sk GLWESecretKey[T]) Fourier // ToFourierGLWESecretKeyAssign transforms GLWE secret key to Fourier GLWE secret key and writes it to skOut. func (e *GLWETransformer[T]) ToFourierGLWESecretKeyAssign(skIn GLWESecretKey[T], skOut FourierGLWESecretKey[T]) { for i := 0; i < e.Parameters.glweRank; i++ { - e.FourierEvaluator.ToFourierPolyAssign(skIn.Value[i], skOut.Value[i]) + e.Evaluator.ToFourierPolyAssign(skIn.Value[i], skOut.Value[i]) } } @@ -58,7 +58,7 @@ func (e *GLWETransformer[T]) ToGLWESecretKey(sk FourierGLWESecretKey[T]) GLWESec // ToGLWESecretKeyAssign transforms Fourier GLWE secret key to GLWE secret key and writes it to skOut. func (e *GLWETransformer[T]) ToGLWESecretKeyAssign(skIn FourierGLWESecretKey[T], skOut GLWESecretKey[T]) { for i := 0; i < e.Parameters.glweRank; i++ { - e.FourierEvaluator.ToPolyAssign(skIn.Value[i], skOut.Value[i]) + e.Evaluator.ToPolyAssign(skIn.Value[i], skOut.Value[i]) } } @@ -72,7 +72,7 @@ func (e *GLWETransformer[T]) ToFourierGLWECiphertext(ct GLWECiphertext[T]) Fouri // ToFourierGLWECiphertextAssign transforms GLWE ciphertext to Fourier GLWE ciphertext and writes it to ctOut. func (e *GLWETransformer[T]) ToFourierGLWECiphertextAssign(ctIn GLWECiphertext[T], ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToFourierPolyAssign(ctIn.Value[i], ctOut.Value[i]) + e.Evaluator.ToFourierPolyAssign(ctIn.Value[i], ctOut.Value[i]) } } @@ -86,7 +86,7 @@ func (e *GLWETransformer[T]) ToGLWECiphertext(ct FourierGLWECiphertext[T]) GLWEC // ToGLWECiphertextAssign transforms Fourier GLWE ciphertext to GLWE ciphertext and writes it to ctOut. func (e *GLWETransformer[T]) ToGLWECiphertextAssign(ctIn FourierGLWECiphertext[T], ctOut GLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToPolyAssign(ctIn.Value[i], ctOut.Value[i]) + e.Evaluator.ToPolyAssign(ctIn.Value[i], ctOut.Value[i]) } } diff --git a/tfhe/fourier_glwe_enc.go b/tfhe/fourier_glwe_enc.go index b721101..25d642b 100644 --- a/tfhe/fourier_glwe_enc.go +++ b/tfhe/fourier_glwe_enc.go @@ -143,12 +143,12 @@ func (e *Encryptor[T]) EncryptFourierGGSWPlaintext(pt GLWEPlaintext[T], gadgetPa // EncryptFourierGGSWPlaintextAssign encrypts GLWE plaintext to FourierGGSW ciphertext and writes it to ctOut. func (e *Encryptor[T]) EncryptFourierGGSWPlaintextAssign(pt GLWEPlaintext[T], ctOut FourierGGSWCiphertext[T]) { e.EncryptFourierGLevPlaintextAssign(pt, ctOut.Value[0]) - for i := 1; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.PolyMulBinaryAssign(e.SecretKey.FourierGLWEKey.Value[i-1], pt.Value, e.buffer.ptGGSW) + for i := 0; i < e.Parameters.glweRank; i++ { + e.Evaluator.BinaryFourierMulAssign(pt.Value, e.SecretKey.FourierGLWEKey.Value[i], e.buffer.ptGGSW) for j := 0; j < ctOut.GadgetParameters.level; j++ { e.PolyEvaluator.ScalarMulAssign(e.buffer.ptGGSW, ctOut.GadgetParameters.BaseQ(j), e.buffer.ctGLWE.Value[0]) e.EncryptGLWEBody(e.buffer.ctGLWE) - e.ToFourierGLWECiphertextAssign(e.buffer.ctGLWE, ctOut.Value[i].Value[j]) + e.ToFourierGLWECiphertextAssign(e.buffer.ctGLWE, ctOut.Value[i+1].Value[j]) } } } diff --git a/tfhe/fourier_glwe_ops.go b/tfhe/fourier_glwe_ops.go index 82c7982..c914d9f 100644 --- a/tfhe/fourier_glwe_ops.go +++ b/tfhe/fourier_glwe_ops.go @@ -14,7 +14,7 @@ func (e *Evaluator[T]) AddFourierGLWE(ct0, ct1 FourierGLWECiphertext[T]) Fourier // AddFourierGLWEAssign computes ctOut = ct0 + ct1. func (e *Evaluator[T]) AddFourierGLWEAssign(ct0, ct1, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.AddAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) + e.PolyEvaluator.AddFourierAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) } } @@ -28,7 +28,7 @@ func (e *Evaluator[T]) SubFourierGLWE(ct0, ct1 FourierGLWECiphertext[T]) Fourier // SubFourierGLWEAssign computes ctOut = ct0 - ct1. func (e *Evaluator[T]) SubFourierGLWEAssign(ct0, ct1, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.SubAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) + e.PolyEvaluator.SubFourierAssign(ct0.Value[i], ct1.Value[i], ctOut.Value[i]) } } @@ -42,7 +42,7 @@ func (e *Evaluator[T]) NegFourierGLWE(ct0 FourierGLWECiphertext[T]) FourierGLWEC // NegFourierGLWEAssign computes ctOut = -ct0. func (e *Evaluator[T]) NegFourierGLWEAssign(ct0, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.NegAssign(ct0.Value[i], ctOut.Value[i]) + e.PolyEvaluator.NegFourierAssign(ct0.Value[i], ctOut.Value[i]) } } @@ -56,21 +56,21 @@ func (e *Evaluator[T]) FloatMulFourierGLWE(ct0 FourierGLWECiphertext[T], c float // FloatMulFourierGLWEAssign computes ctOut = c * ct0. func (e *Evaluator[T]) FloatMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c float64, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.FloatMulAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.FloatMulFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // FloatMulAddFourierGLWEAssign computes ctOut += c * ct0. func (e *Evaluator[T]) FloatMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c float64, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.FloatMulAddAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.FloatMulAddFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // FloatMulSubFourierGLWEAssign computes ctOut -= c * ct0. func (e *Evaluator[T]) FloatMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c float64, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.FloatMulSubAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.FloatMulSubFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } @@ -84,21 +84,21 @@ func (e *Evaluator[T]) CmplxMulFourierGLWE(ct0 FourierGLWECiphertext[T], c compl // CmplxMulFourierGLWEAssign computes ctOut = c * ct0. func (e *Evaluator[T]) CmplxMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c complex128, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.CmplxMulAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.CmplxMulFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // CmplxMulAddFourierGLWEAssign computes ctOut += c * ct0. func (e *Evaluator[T]) CmplxMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c complex128, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.CmplxMulAddAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.CmplxMulAddFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } // CmplxMulSubFourierGLWEAssign computes ctOut -= c * ct0. func (e *Evaluator[T]) CmplxMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], c complex128, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.CmplxMulSubAssign(ct0.Value[i], c, ctOut.Value[i]) + e.PolyEvaluator.CmplxMulSubFourierAssign(ct0.Value[i], c, ctOut.Value[i]) } } @@ -111,25 +111,25 @@ func (e *Evaluator[T]) PolyMulFourierGLWE(ct0 FourierGLWECiphertext[T], p poly.P // PolyMulFourierGLWEAssign computes ctOut = p * ct0. func (e *Evaluator[T]) PolyMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], p poly.Poly[T], ctOut FourierGLWECiphertext[T]) { - e.FourierEvaluator.ToFourierPolyAssign(p, e.buffer.fpOut) + e.PolyEvaluator.ToFourierPolyAssign(p, e.buffer.fpMul) for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.MulAssign(ct0.Value[i], e.buffer.fpOut, ctOut.Value[i]) + e.PolyEvaluator.MulFourierAssign(ct0.Value[i], e.buffer.fpMul, ctOut.Value[i]) } } // PolyMulAddFourierGLWEAssign computes ctOut += p * ct0. func (e *Evaluator[T]) PolyMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], p poly.Poly[T], ctOut FourierGLWECiphertext[T]) { - e.FourierEvaluator.ToFourierPolyAssign(p, e.buffer.fpOut) + e.PolyEvaluator.ToFourierPolyAssign(p, e.buffer.fpMul) for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.MulAddAssign(ct0.Value[i], e.buffer.fpOut, ctOut.Value[i]) + e.PolyEvaluator.MulAddFourierAssign(ct0.Value[i], e.buffer.fpMul, ctOut.Value[i]) } } // PolyMulSubFourierGLWEAssign computes ctOut -= p * ct0. func (e *Evaluator[T]) PolyMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], p poly.Poly[T], ctOut FourierGLWECiphertext[T]) { - e.FourierEvaluator.ToFourierPolyAssign(p, e.buffer.fpOut) + e.PolyEvaluator.ToFourierPolyAssign(p, e.buffer.fpMul) for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.MulSubAssign(ct0.Value[i], e.buffer.fpOut, ctOut.Value[i]) + e.PolyEvaluator.MulSubFourierAssign(ct0.Value[i], e.buffer.fpMul, ctOut.Value[i]) } } @@ -143,20 +143,20 @@ func (e *Evaluator[T]) FourierPolyMulFourierGLWE(ct0 FourierGLWECiphertext[T], f // FourierPolyMulFourierGLWEAssign computes ctOut = fp * ct0. func (e *Evaluator[T]) FourierPolyMulFourierGLWEAssign(ct0 FourierGLWECiphertext[T], fp poly.FourierPoly, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.MulAssign(ct0.Value[i], fp, ctOut.Value[i]) + e.PolyEvaluator.MulFourierAssign(ct0.Value[i], fp, ctOut.Value[i]) } } // FourierPolyMulAddFourierGLWEAssign computes ctOut += fp * ct0. func (e *Evaluator[T]) FourierPolyMulAddFourierGLWEAssign(ct0 FourierGLWECiphertext[T], fp poly.FourierPoly, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.MulAddAssign(ct0.Value[i], fp, ctOut.Value[i]) + e.PolyEvaluator.MulAddFourierAssign(ct0.Value[i], fp, ctOut.Value[i]) } } // FourierPolyMulSubFourierGLWEAssign computes ctOut -= fp * ct0. func (e *Evaluator[T]) FourierPolyMulSubFourierGLWEAssign(ct0 FourierGLWECiphertext[T], fp poly.FourierPoly, ctOut FourierGLWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.MulSubAssign(ct0.Value[i], fp, ctOut.Value[i]) + e.PolyEvaluator.MulSubFourierAssign(ct0.Value[i], fp, ctOut.Value[i]) } } diff --git a/tfhe/glwe_enc.go b/tfhe/glwe_enc.go index 657dc13..8fafaf4 100644 --- a/tfhe/glwe_enc.go +++ b/tfhe/glwe_enc.go @@ -34,9 +34,9 @@ func (e *Encryptor[T]) EncryptGLWEPlaintextAssign(pt GLWEPlaintext[T], ctOut GLW // EncryptGLWEBody encrypts the value in the body of GLWE ciphertext and overrides it. // This avoids the need for most buffers. func (e *Encryptor[T]) EncryptGLWEBody(ct GLWECiphertext[T]) { - for i := 1; i < e.Parameters.glweRank+1; i++ { - e.UniformSampler.SampleSliceAssign(ct.Value[i].Coeffs) - e.FourierEvaluator.PolyMulBinarySubAssign(e.SecretKey.FourierGLWEKey.Value[i-1], ct.Value[i], ct.Value[0]) + for i := 0; i < e.Parameters.glweRank; i++ { + e.UniformSampler.SampleSliceAssign(ct.Value[i+1].Coeffs) + e.Evaluator.BinaryFourierMulSubAssign(ct.Value[i+1], e.SecretKey.FourierGLWEKey.Value[i], ct.Value[0]) } e.GaussianSampler.SampleSliceAddAssign(e.Parameters.GLWEStdDevQ(), ct.Value[0].Coeffs) @@ -65,7 +65,7 @@ func (e *Encryptor[T]) DecryptGLWEPlaintext(ct GLWECiphertext[T]) GLWEPlaintext[ func (e *Encryptor[T]) DecryptGLWEPlaintextAssign(ct GLWECiphertext[T], ptOut GLWEPlaintext[T]) { ptOut.Value.CopyFrom(ct.Value[0]) for i := 0; i < e.Parameters.glweRank; i++ { - e.FourierEvaluator.PolyMulBinaryAddAssign(e.SecretKey.FourierGLWEKey.Value[i], ct.Value[i+1], ptOut.Value) + e.Evaluator.BinaryFourierMulAddAssign(ct.Value[i+1], e.SecretKey.FourierGLWEKey.Value[i], ptOut.Value) } } @@ -162,11 +162,11 @@ func (e *Encryptor[T]) EncryptGGSWPlaintext(pt GLWEPlaintext[T], gadgetParams Ga // EncryptGGSWPlaintextAssign encrypts GLWE plaintext to GGSW ciphertext and writes it to ctOut. func (e *Encryptor[T]) EncryptGGSWPlaintextAssign(pt GLWEPlaintext[T], ctOut GGSWCiphertext[T]) { e.EncryptGLevPlaintextAssign(pt, ctOut.Value[0]) - for i := 1; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.PolyMulBinaryAssign(e.SecretKey.FourierGLWEKey.Value[i-1], pt.Value, e.buffer.ptGGSW) + for i := 0; i < e.Parameters.glweRank; i++ { + e.Evaluator.BinaryFourierMulAssign(pt.Value, e.SecretKey.FourierGLWEKey.Value[i], e.buffer.ptGGSW) for j := 0; j < ctOut.GadgetParameters.level; j++ { - e.PolyEvaluator.ScalarMulAssign(e.buffer.ptGGSW, ctOut.GadgetParameters.BaseQ(j), ctOut.Value[i].Value[j].Value[0]) - e.EncryptGLWEBody(ctOut.Value[i].Value[j]) + e.PolyEvaluator.ScalarMulAssign(e.buffer.ptGGSW, ctOut.GadgetParameters.BaseQ(j), ctOut.Value[i+1].Value[j].Value[0]) + e.EncryptGLWEBody(ctOut.Value[i+1].Value[j]) } } } diff --git a/tfhe/glwe_enc_public.go b/tfhe/glwe_enc_public.go index e721b05..3e4f612 100644 --- a/tfhe/glwe_enc_public.go +++ b/tfhe/glwe_enc_public.go @@ -34,13 +34,13 @@ func (e *PublicEncryptor[T]) EncryptGLWEBody(ct GLWECiphertext[T]) { } e.ToFourierGLWESecretKeyAssign(e.buffer.auxKey, e.buffer.auxFourierKey) - e.FourierEvaluator.PolyMulBinaryAddAssign(e.buffer.auxFourierKey.Value[0], e.PublicKey.GLWEKey.Value[0].Value[0], ct.Value[0]) + e.Evaluator.BinaryFourierMulAddAssign(e.PublicKey.GLWEKey.Value[0].Value[0], e.buffer.auxFourierKey.Value[0], ct.Value[0]) for j := 1; j < e.Parameters.glweRank+1; j++ { - e.FourierEvaluator.PolyMulBinaryAddAssign(e.buffer.auxFourierKey.Value[0], e.PublicKey.GLWEKey.Value[0].Value[j], ct.Value[j]) + e.Evaluator.BinaryFourierMulAddAssign(e.PublicKey.GLWEKey.Value[0].Value[j], e.buffer.auxFourierKey.Value[0], ct.Value[j]) } for i := 1; i < e.Parameters.glweRank; i++ { for j := 0; j < e.Parameters.glweRank+1; j++ { - e.FourierEvaluator.PolyMulBinaryAddAssign(e.buffer.auxFourierKey.Value[i], e.PublicKey.GLWEKey.Value[i].Value[j], ct.Value[j]) + e.Evaluator.BinaryFourierMulAddAssign(e.PublicKey.GLWEKey.Value[i].Value[j], e.buffer.auxFourierKey.Value[i], ct.Value[j]) } } diff --git a/tfhe/glwe_ops.go b/tfhe/glwe_ops.go index b4a0aeb..949257e 100644 --- a/tfhe/glwe_ops.go +++ b/tfhe/glwe_ops.go @@ -132,6 +132,34 @@ func (e *Evaluator[T]) PolyMulSubGLWEAssign(ct0 GLWECiphertext[T], p poly.Poly[T } } +// FourierPolyMulGLWE returns fp * ct0. +func (e *Evaluator[T]) FourierPolyMulGLWE(ct0 GLWECiphertext[T], fp poly.FourierPoly) GLWECiphertext[T] { + ctOut := NewGLWECiphertext(e.Parameters) + e.FourierPolyMulGLWEAssign(ct0, fp, ctOut) + return ctOut +} + +// FourierPolyMulGLWEAssign computes ctOut = fp * ct0. +func (e *Evaluator[T]) FourierPolyMulGLWEAssign(ct0 GLWECiphertext[T], fp poly.FourierPoly, ctOut GLWECiphertext[T]) { + for i := 0; i < e.Parameters.glweRank+1; i++ { + e.PolyEvaluator.FourierMulAssign(ct0.Value[i], fp, ctOut.Value[i]) + } +} + +// FourierPolyMulAddGLWEAssign computes ctOut += fp * ct0. +func (e *Evaluator[T]) FourierPolyMulAddGLWEAssign(ct0 GLWECiphertext[T], fp poly.FourierPoly, ctOut GLWECiphertext[T]) { + for i := 0; i < e.Parameters.glweRank+1; i++ { + e.PolyEvaluator.FourierMulAddAssign(ct0.Value[i], fp, ctOut.Value[i]) + } +} + +// FourierPolyMulSubGLWEAssign computes ctOut -= fp * ct0. +func (e *Evaluator[T]) FourierPolyMulSubGLWEAssign(ct0 GLWECiphertext[T], fp poly.FourierPoly, ctOut GLWECiphertext[T]) { + for i := 0; i < e.Parameters.glweRank+1; i++ { + e.PolyEvaluator.FourierMulSubAssign(ct0.Value[i], fp, ctOut.Value[i]) + } +} + // MonomialMulGLWE returns X^d * ct0. func (e *Evaluator[T]) MonomialMulGLWE(ct0 GLWECiphertext[T], d int) GLWECiphertext[T] { ctOut := NewGLWECiphertext(e.Parameters) diff --git a/tfhe/lwe_enc.go b/tfhe/lwe_enc.go index 6428825..719d471 100644 --- a/tfhe/lwe_enc.go +++ b/tfhe/lwe_enc.go @@ -109,10 +109,10 @@ func (e *Encryptor[T]) EncryptGSWPlaintext(pt LWEPlaintext[T], gadgetParams Gadg func (e *Encryptor[T]) EncryptGSWPlaintextAssign(pt LWEPlaintext[T], ctOut GSWCiphertext[T]) { e.EncryptLevPlaintextAssign(pt, ctOut.Value[0]) - for i := 1; i < e.Parameters.DefaultLWEDimension()+1; i++ { + for i := 0; i < e.Parameters.DefaultLWEDimension(); i++ { for j := 0; j < ctOut.GadgetParameters.level; j++ { - ctOut.Value[i].Value[j].Value[0] = e.DefaultLWESecretKey().Value[i-1] * pt.Value << ctOut.GadgetParameters.BaseQLog(j) - e.EncryptLWEBody(ctOut.Value[i].Value[j]) + ctOut.Value[i+1].Value[j].Value[0] = e.DefaultLWESecretKey().Value[i] * pt.Value << ctOut.GadgetParameters.BaseQLog(j) + e.EncryptLWEBody(ctOut.Value[i+1].Value[j]) } } } diff --git a/tfhe/lwe_enc_public.go b/tfhe/lwe_enc_public.go index 9dbf682..cbf1a31 100644 --- a/tfhe/lwe_enc_public.go +++ b/tfhe/lwe_enc_public.go @@ -49,7 +49,7 @@ func (e *PublicEncryptor[T]) EncryptLWEBody(ct LWECiphertext[T]) { for i := 0; i < e.Parameters.glweRank; i++ { for j := 0; j < e.Parameters.glweRank; j++ { - e.FourierEvaluator.PolyMulBinaryAddAssign(e.buffer.auxFourierKey.Value[i], e.PublicKey.LWEKey.Value[i].Value[j+1], ctGLWE[j]) + e.Evaluator.BinaryFourierMulAddAssign(e.PublicKey.LWEKey.Value[i].Value[j+1], e.buffer.auxFourierKey.Value[i], ctGLWE[j]) } } diff --git a/tfhe/product.go b/tfhe/product.go index 828ef16..2a7f8b0 100644 --- a/tfhe/product.go +++ b/tfhe/product.go @@ -57,7 +57,7 @@ func (e *Evaluator[T]) GadgetProductAssignGLWE(ctFourierGLev FourierGLevCipherte e.DecomposePolyAssign(p, ctFourierGLev.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierGLev.GadgetParameters.level; i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } e.FourierPolyMulFourierGLWEAssign(ctFourierGLev.Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd) @@ -66,7 +66,7 @@ func (e *Evaluator[T]) GadgetProductAssignGLWE(ctFourierGLev FourierGLevCipherte } for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) + e.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) } } @@ -77,7 +77,7 @@ func (e *Evaluator[T]) GadgetProductAddAssignGLWE(ctFourierGLev FourierGLevCiphe e.DecomposePolyAssign(p, ctFourierGLev.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierGLev.GadgetParameters.level; i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } e.FourierPolyMulFourierGLWEAssign(ctFourierGLev.Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd) @@ -86,7 +86,7 @@ func (e *Evaluator[T]) GadgetProductAddAssignGLWE(ctFourierGLev FourierGLevCiphe } for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) } } @@ -97,7 +97,7 @@ func (e *Evaluator[T]) GadgetProductSubAssignGLWE(ctFourierGLev FourierGLevCiphe e.DecomposePolyAssign(p, ctFourierGLev.GadgetParameters, polyDecomposed) for i := 0; i < ctFourierGLev.GadgetParameters.level; i++ { - e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) + e.PolyEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i]) } e.FourierPolyMulFourierGLWEAssign(ctFourierGLev.Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd) @@ -106,7 +106,7 @@ func (e *Evaluator[T]) GadgetProductSubAssignGLWE(ctFourierGLev FourierGLevCiphe } for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) + e.PolyEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) } } @@ -206,7 +206,7 @@ func (e *Evaluator[T]) ExternalProductGLWEAssign(ctFourierGGSW FourierGGSWCipher } for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) + e.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) } } @@ -228,7 +228,7 @@ func (e *Evaluator[T]) ExternalProductAddGLWEAssign(ctFourierGGSW FourierGGSWCip } for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) + e.PolyEvaluator.ToPolyAddAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) } } @@ -250,7 +250,7 @@ func (e *Evaluator[T]) ExternalProductSubGLWEAssign(ctFourierGGSW FourierGGSWCip } for i := 0; i < e.Parameters.glweRank+1; i++ { - e.FourierEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) + e.PolyEvaluator.ToPolySubAssignUnsafe(e.buffer.ctFourierProd.Value[i], ctGLWEOut.Value[i]) } }