Skip to content

Commit

Permalink
feat: Create Decomposer
Browse files Browse the repository at this point in the history
  • Loading branch information
sp301415 committed Aug 28, 2024
1 parent 06c99be commit dd28b9f
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 118 deletions.
22 changes: 0 additions & 22 deletions mktfhe/decompose.go

This file was deleted.

68 changes: 63 additions & 5 deletions mktfhe/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type Evaluator[T tfhe.TorusInt] struct {
*tfhe.Encoder[T]
// GLWETansformer is an embedded GLWETransformer for this Evaluator.
*GLWETransformer[T]
// Decomposer is an embedded Decomposer for this Evaluator.
*tfhe.Decomposer[T]
// BaseSingleKeyEvaluator is a single-key Evaluator for this Evaluator.
// This is always guaranteed to be a working, non-nil evaluator without a key.
BaseSingleKeyEvaluator *tfhe.Evaluator[T]
Expand All @@ -37,13 +39,17 @@ type Evaluator[T tfhe.TorusInt] struct {

// evaluationBuffer is a buffer for evaluation.
type evaluationBuffer[T tfhe.TorusInt] struct {
// polyFourierDecomposed holds the decomposed polynomial in Fourier domain.
// Initially has length bootstrapParameters.level.
// Use [*Evaluator.polyFourierDecomposedBuffer] to get appropriate length of buffer.
polyFourierDecomposed []poly.FourierPoly
// decomposed holds the decomposed scalar.
// Has length keyswitchParameters.level.
decomposed []T
// polyDecomposed holds the decomposed polynomial.
// Initially has length relinKeyParameters.level.
// Use [*Evaluator.polyDecomposedBuffer] to get appropriate length of buffer.
polyDecomposed []poly.Poly[T]
// polyFourierDecomposed holds the decomposed polynomial in Fourier domain.
// Initially has length relinKeyParameters.level.
// Use [*Evaluator.polyFourierDecomposedBuffer] to get appropriate length of buffer.
polyFourierDecomposed []poly.FourierPoly

// ctProd holds the intermediate value in Hybrid Product.
ctProd GLWECiphertext[T]
Expand Down Expand Up @@ -95,6 +101,7 @@ func NewEvaluator[T tfhe.TorusInt](params Parameters[T], evk map[int]EvaluationK
return &Evaluator[T]{
Encoder: tfhe.NewEncoder(params.Parameters),
GLWETransformer: NewGLWETransformer(params),
Decomposer: tfhe.NewDecomposer[T](),
BaseSingleKeyEvaluator: tfhe.NewEvaluator(params.Parameters, tfhe.EvaluationKey[T]{}),
SingleKeyEvaluators: singleEvals,

Expand All @@ -109,8 +116,10 @@ func NewEvaluator[T tfhe.TorusInt](params Parameters[T], evk map[int]EvaluationK

// newEvaluationBuffer allocates an empty evaluationBuffer.
func newEvaluationBuffer[T tfhe.TorusInt](params Parameters[T]) evaluationBuffer[T] {
polyDecomposed := make([]poly.Poly[T], params.relinKeyParameters.Level())
polyFourierDecomposed := make([]poly.FourierPoly, params.relinKeyParameters.Level())
for i := 0; i < params.relinKeyParameters.Level(); i++ {
polyDecomposed[i] = poly.NewPoly[T](params.PolyDegree())
polyFourierDecomposed[i] = poly.NewFourierPoly(params.PolyDegree())
}

Expand Down Expand Up @@ -139,8 +148,9 @@ func newEvaluationBuffer[T tfhe.TorusInt](params Parameters[T]) evaluationBuffer
}

return evaluationBuffer[T]{
polyFourierDecomposed: polyFourierDecomposed,
decomposed: make([]T, params.KeySwitchParameters().Level()),
polyDecomposed: polyDecomposed,
polyFourierDecomposed: polyFourierDecomposed,

ctProd: NewGLWECiphertext(params),
ctFourierProd: NewFourierGLWECiphertext(params),
Expand Down Expand Up @@ -185,6 +195,7 @@ func (e *Evaluator[T]) ShallowCopy() *Evaluator[T] {
return &Evaluator[T]{
Encoder: e.Encoder,
GLWETransformer: e.GLWETransformer.ShallowCopy(),
Decomposer: e.Decomposer,
BaseSingleKeyEvaluator: e.BaseSingleKeyEvaluator.ShallowCopy(),
SingleKeyEvaluators: singleEvals,

Expand All @@ -196,3 +207,50 @@ func (e *Evaluator[T]) ShallowCopy() *Evaluator[T] {
buffer: newEvaluationBuffer(e.Parameters),
}
}

/*
// decomposedBuffer returns the decomposed buffer of Evaluator.
// if len(decomposed) >= Level, it returns the subslice of the buffer.
// otherwise, it extends the buffer of the Evaluator and returns it.
func (e *Evaluator[T]) decomposedBuffer(gadgetParams tfhe.GadgetParameters[T]) []T {
if len(e.buffer.decomposed) >= gadgetParams.Level() {
return e.buffer.decomposed[:gadgetParams.Level()]
}
oldLen := len(e.buffer.decomposed)
e.buffer.decomposed = append(e.buffer.decomposed, make([]T, gadgetParams.Level()-oldLen)...)
return e.buffer.decomposed
}
*/

// polyDecomposedBuffer returns the polyDecomposed buffer of Evaluator.
// if len(polyDecomposed) >= Level, it returns the subslice of the buffer.
// otherwise, it extends the buffer of the Evaluator and returns it.
func (e *Evaluator[T]) polyDecomposedBuffer(gadgetParams tfhe.GadgetParameters[T]) []poly.Poly[T] {
if len(e.buffer.polyDecomposed) >= gadgetParams.Level() {
return e.buffer.polyDecomposed[:gadgetParams.Level()]
}

oldLen := len(e.buffer.polyDecomposed)
e.buffer.polyDecomposed = append(e.buffer.polyDecomposed, make([]poly.Poly[T], gadgetParams.Level()-oldLen)...)
for i := oldLen; i < gadgetParams.Level(); i++ {
e.buffer.polyDecomposed[i] = poly.NewPoly[T](e.Parameters.PolyDegree())
}
return e.buffer.polyDecomposed
}

// polyFourierDecomposedBuffer returns the fourierPolyDecomposed buffer of Evaluator.
// if len(fourierPolyDecomposed) >= Level, it returns the subslice of the buffer.
// otherwise, it extends the buffer of the Evaluator and returns it.
func (e *Evaluator[T]) polyFourierDecomposedBuffer(gadgetParams tfhe.GadgetParameters[T]) []poly.FourierPoly {
if len(e.buffer.polyFourierDecomposed) >= gadgetParams.Level() {
return e.buffer.polyFourierDecomposed[:gadgetParams.Level()]
}

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] = poly.NewFourierPoly(e.Parameters.PolyDegree())
}
return e.buffer.polyFourierDecomposed
}
42 changes: 33 additions & 9 deletions mktfhe/product.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ func (e *Evaluator[T]) HybridProduct(idx int, ctFourierUniEnc FourierUniEncrypti
func (e *Evaluator[T]) HybridProductAssign(idx int, ctFourierUniEnc FourierUniEncryption[T], ctGLWE, ctGLWEOut GLWECiphertext[T]) {
eIdx := e.SingleKeyEvaluators[idx]

polyDecomposed := e.polyDecomposedBuffer(ctFourierUniEnc.GadgetParameters)
polyFourierDecomposed := e.polyFourierDecomposedBuffer(ctFourierUniEnc.GadgetParameters)

eIdx.FourierDecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
e.DecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyDecomposed)
for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ {
e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i])
}

eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0])
for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ {
Expand All @@ -32,7 +36,10 @@ func (e *Evaluator[T]) HybridProductAssign(idx int, ctFourierUniEnc FourierUniEn

for i, ok := range e.PartyBitMap {
if ok {
eIdx.FourierDecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
e.DecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyDecomposed)
for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ {
e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i])
}

eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1])
for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ {
Expand All @@ -46,8 +53,9 @@ func (e *Evaluator[T]) HybridProductAssign(idx int, ctFourierUniEnc FourierUniEn
}

eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle)
eIdx.FourierDecomposePolyAssign(e.buffer.ctProdSingle, ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
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])
}
Expand All @@ -66,9 +74,13 @@ func (e *Evaluator[T]) HybridProductAssign(idx int, ctFourierUniEnc FourierUniEn
func (e *Evaluator[T]) HybridProductAddAssign(idx int, ctFourierUniEnc FourierUniEncryption[T], ctGLWE, ctGLWEOut GLWECiphertext[T]) {
eIdx := e.SingleKeyEvaluators[idx]

polyDecomposed := e.polyDecomposedBuffer(ctFourierUniEnc.GadgetParameters)
polyFourierDecomposed := e.polyFourierDecomposedBuffer(ctFourierUniEnc.GadgetParameters)

eIdx.FourierDecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
e.DecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyDecomposed)
for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ {
e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i])
}

eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0])
for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ {
Expand All @@ -83,7 +95,10 @@ func (e *Evaluator[T]) HybridProductAddAssign(idx int, ctFourierUniEnc FourierUn

for i, ok := range e.PartyBitMap {
if ok {
eIdx.FourierDecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
e.DecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyDecomposed)
for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ {
e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i])
}

eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1])
for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ {
Expand All @@ -97,8 +112,9 @@ func (e *Evaluator[T]) HybridProductAddAssign(idx int, ctFourierUniEnc FourierUn
}

eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle)
eIdx.FourierDecomposePolyAssign(e.buffer.ctProdSingle, ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
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])
}
Expand All @@ -115,9 +131,13 @@ func (e *Evaluator[T]) HybridProductAddAssign(idx int, ctFourierUniEnc FourierUn
func (e *Evaluator[T]) HybridProductSubAssign(idx int, ctFourierUniEnc FourierUniEncryption[T], ctGLWE, ctGLWEOut GLWECiphertext[T]) {
eIdx := e.SingleKeyEvaluators[idx]

polyDecomposed := e.polyDecomposedBuffer(ctFourierUniEnc.GadgetParameters)
polyFourierDecomposed := e.polyFourierDecomposedBuffer(ctFourierUniEnc.GadgetParameters)

eIdx.FourierDecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
e.DecomposePolyAssign(ctGLWE.Value[0], ctFourierUniEnc.GadgetParameters, polyDecomposed)
for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ {
e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i])
}

eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[0])
for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ {
Expand All @@ -132,7 +152,10 @@ func (e *Evaluator[T]) HybridProductSubAssign(idx int, ctFourierUniEnc FourierUn

for i, ok := range e.PartyBitMap {
if ok {
eIdx.FourierDecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
e.DecomposePolyAssign(ctGLWE.Value[i+1], ctFourierUniEnc.GadgetParameters, polyDecomposed)
for i := 0; i < ctFourierUniEnc.GadgetParameters.Level(); i++ {
e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], polyFourierDecomposed[i])
}

eIdx.FourierEvaluator.MulAssign(ctFourierUniEnc.Value[0].Value[0].Value[0], polyFourierDecomposed[0], e.buffer.ctFourierProd.Value[i+1])
for j := 1; j < ctFourierUniEnc.GadgetParameters.Level(); j++ {
Expand All @@ -146,8 +169,9 @@ func (e *Evaluator[T]) HybridProductSubAssign(idx int, ctFourierUniEnc FourierUn
}

eIdx.FourierEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierProdSingle, e.buffer.ctProdSingle)
eIdx.FourierDecomposePolyAssign(e.buffer.ctProdSingle, ctFourierUniEnc.GadgetParameters, polyFourierDecomposed)
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])
}
Expand Down
92 changes: 18 additions & 74 deletions tfhe/decompose.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,26 @@ import (
"github.com/sp301415/tfhe-go/math/poly"
)

// Decomposer decomposes a scalar or a polynomial
// according to the gadget parameters.
//
// Decomposer is safe for concurrent use.
type Decomposer[T TorusInt] struct{}

// NewDecomposer creates a new Decomposer.
func NewDecomposer[T TorusInt]() *Decomposer[T] {
return &Decomposer[T]{}
}

// Decompose decomposes x with respect to gadgetParams.
func (e *Evaluator[T]) Decompose(x T, gadgetParams GadgetParameters[T]) []T {
func (d *Decomposer[T]) Decompose(x T, gadgetParams GadgetParameters[T]) []T {
decomposedOut := make([]T, gadgetParams.level)
e.DecomposeAssign(x, gadgetParams, decomposedOut)
d.DecomposeAssign(x, gadgetParams, decomposedOut)
return decomposedOut
}

// DecomposeAssign decomposes x with respect to gadgetParams and writes it to decomposedOut.
func (e *Evaluator[T]) DecomposeAssign(x T, gadgetParams GadgetParameters[T], decomposedOut []T) {
func (d *Decomposer[T]) DecomposeAssign(x T, gadgetParams GadgetParameters[T], decomposedOut []T) {
lastBaseQLog := gadgetParams.LastBaseQLog()
u := num.DivRoundBits(x, lastBaseQLog)
for i := gadgetParams.level - 1; i >= 1; i-- {
Expand All @@ -27,83 +38,16 @@ func (e *Evaluator[T]) DecomposeAssign(x T, gadgetParams GadgetParameters[T], de
}

// DecomposePoly decomposes p with respect to gadgetParams.
func (e *Evaluator[T]) DecomposePoly(p poly.Poly[T], gadgetParams GadgetParameters[T]) []poly.Poly[T] {
func (d *Decomposer[T]) DecomposePoly(p poly.Poly[T], gadgetParams GadgetParameters[T]) []poly.Poly[T] {
decomposedOut := make([]poly.Poly[T], gadgetParams.level)
for i := 0; i < gadgetParams.level; i++ {
decomposedOut[i] = e.PolyEvaluator.NewPoly()
decomposedOut[i] = poly.NewPoly[T](p.Degree())
}
e.DecomposePolyAssign(p, gadgetParams, decomposedOut)
d.DecomposePolyAssign(p, gadgetParams, decomposedOut)
return decomposedOut
}

// DecomposePolyAssign decomposes p with respect to gadgetParams and writes it to decomposedOut.
func (e *Evaluator[T]) DecomposePolyAssign(p poly.Poly[T], gadgetParams GadgetParameters[T], decomposedOut []poly.Poly[T]) {
func (d *Decomposer[T]) DecomposePolyAssign(p poly.Poly[T], gadgetParams GadgetParameters[T], decomposedOut []poly.Poly[T]) {
decomposePolyAssign(p, gadgetParams, decomposedOut)
}

// FourierDecomposePoly decomposes p with respect to gadgetParams, and transforms it to the Fourier domain.
func (e *Evaluator[T]) FourierDecomposePoly(p poly.Poly[T], gadgetParams GadgetParameters[T]) []poly.FourierPoly {
decomposedOut := make([]poly.FourierPoly, gadgetParams.level)
for i := 0; i < gadgetParams.level; i++ {
decomposedOut[i] = e.FourierEvaluator.NewFourierPoly()
}
e.FourierDecomposePolyAssign(p, gadgetParams, decomposedOut)
return decomposedOut
}

// FourierDecomposePolyAssign decomposes p with respect to gadgetParams,
// transforms it to the Fourier domain,
// and writes it to decomposedOut.
func (e *Evaluator[T]) FourierDecomposePolyAssign(p poly.Poly[T], gadgetParams GadgetParameters[T], decomposedOut []poly.FourierPoly) {
polyDecomposed := e.polyDecomposedBuffer(gadgetParams)
decomposePolyAssign(p, gadgetParams, polyDecomposed)

for i := 0; i < gadgetParams.level; i++ {
e.FourierEvaluator.ToFourierPolyAssign(polyDecomposed[i], decomposedOut[i])
}
}

// polyDecomposedBuffer returns the polyDecomposed buffer of Evaluator.
// if len(polyDecomposed) >= Level, it returns the subslice of the buffer.
// otherwise, it extends the buffer of the Evaluator and returns it.
func (e *Evaluator[T]) polyDecomposedBuffer(gadgetParams GadgetParameters[T]) []poly.Poly[T] {
if len(e.buffer.polyDecomposed) >= gadgetParams.level {
return e.buffer.polyDecomposed[:gadgetParams.level]
}

oldLen := len(e.buffer.polyDecomposed)
e.buffer.polyDecomposed = append(e.buffer.polyDecomposed, make([]poly.Poly[T], gadgetParams.level-oldLen)...)
for i := oldLen; i < gadgetParams.level; i++ {
e.buffer.polyDecomposed[i] = e.PolyEvaluator.NewPoly()
}
return e.buffer.polyDecomposed
}

// polyFourierDecomposedBuffer returns the fourierPolyDecomposed buffer of Evaluator.
// if len(fourierPolyDecomposed) >= Level, it returns the subslice of the buffer.
// otherwise, it extends the buffer of the Evaluator and returns it.
func (e *Evaluator[T]) polyFourierDecomposedBuffer(gadgetParams GadgetParameters[T]) []poly.FourierPoly {
if len(e.buffer.polyFourierDecomposed) >= gadgetParams.level {
return e.buffer.polyFourierDecomposed[:gadgetParams.level]
}

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()
}
return e.buffer.polyFourierDecomposed
}

// decomposedBuffer returns the decomposed buffer of Evaluator.
// if len(decomposed) >= Level, it returns the subslice of the buffer.
// otherwise, it extends the buffer of the Evaluator and returns it.
func (e *Evaluator[T]) decomposedBuffer(gadgetParams GadgetParameters[T]) []T {
if len(e.buffer.decomposed) >= gadgetParams.level {
return e.buffer.decomposed[:gadgetParams.level]
}

oldLen := len(e.buffer.decomposed)
e.buffer.decomposed = append(e.buffer.decomposed, make([]T, gadgetParams.level-oldLen)...)
return e.buffer.decomposed
}
Loading

0 comments on commit dd28b9f

Please sign in to comment.