Skip to content

Commit

Permalink
permutations as part of linear transformations
Browse files Browse the repository at this point in the history
  • Loading branch information
qantik committed Jun 19, 2024
1 parent 27cb1b2 commit a4e50f6
Show file tree
Hide file tree
Showing 31 changed files with 1,012 additions and 685 deletions.
21 changes: 21 additions & 0 deletions core/rlwe/element.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"io"
"math/bits"

"github.com/google/go-cmp/cmp"
"github.com/tuneinsight/lattigo/v5/ring"
Expand All @@ -15,6 +16,8 @@ import (

// ElementInterface is a common interface for Ciphertext and Plaintext types.
type ElementInterface[T ring.Poly | ringqp.Poly] interface {
N() int
LogN() int
El() *Element[T]
Degree() int
Level() int
Expand Down Expand Up @@ -89,6 +92,24 @@ func NewElementAtLevelFromPoly(level int, poly []ring.Poly) (*Element[ring.Poly]
return &Element[ring.Poly]{Value: Value}, nil
}

// N returns the ring degree used by the target element.
func (op Element[T]) N() int {
switch el := any(op.Value[0]).(type) {
case ring.Poly:
return el.N()
case ringqp.Poly:
return el.Q.N()
default:
// Sanity check
panic("invalid Element[type]")
}
}

// LogN returns the log2 of the ring degree used by the target element.
func (op Element[T]) LogN() int {
return bits.Len64(uint64(op.N() - 1))
}

// Equal performs a deep equal.
func (op Element[T]) Equal(other *Element[T]) bool {
return cmp.Equal(op.MetaData, other.MetaData) && cmp.Equal(op.Value, other.Value)
Expand Down
8 changes: 7 additions & 1 deletion core/rlwe/evaluator_automorphism.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,17 @@ func (eval Evaluator) AutomorphismHoistedLazy(levelQ int, ctIn *Ciphertext, c1De

levelP := evk.LevelP()

if ctQP.LevelP() < levelP {
return fmt.Errorf("ctQP.LevelP()=%d < GaloisKey[%d].LevelP()=%d", ctQP.LevelP(), galEl, levelP)
}

ctTmp := &Element[ringqp.Poly]{}
ctTmp.Value = []ringqp.Poly{eval.BuffQP[0], eval.BuffQP[1]}
ctTmp.MetaData = ctIn.MetaData

eval.GadgetProductHoistedLazy(levelQ, c1DecompQP, &evk.GadgetCiphertext, ctTmp)
if err = eval.GadgetProductHoistedLazy(levelQ, c1DecompQP, &evk.GadgetCiphertext, ctTmp); err != nil {
panic(fmt.Errorf("eval.GadgetProductHoistedLazy: %w", err))
}

ringQP := eval.params.RingQP().AtLevel(levelQ, levelP)

Expand Down
121 changes: 71 additions & 50 deletions core/rlwe/evaluator_gadget_product.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ func (eval Evaluator) GadgetProduct(levelQ int, cx ring.Poly, gadgetCt *GadgetCi
ctTmp.Value = []ringqp.Poly{{Q: ct.Value[0], P: eval.BuffQP[0].P}, {Q: ct.Value[1], P: eval.BuffQP[1].P}}
ctTmp.MetaData = ct.MetaData

eval.GadgetProductLazy(levelQ, cx, gadgetCt, ctTmp)
if err := eval.GadgetProductLazy(levelQ, cx, gadgetCt, ctTmp); err != nil {
panic(fmt.Errorf("eval.GadgetProductLazy: %w", err))
}

eval.ModDown(levelQ, levelP, ctTmp, ct)
}
Expand Down Expand Up @@ -90,26 +92,35 @@ func (eval Evaluator) ModDown(levelQ, levelP int, ctQP *Element[ringqp.Poly], ct

// GadgetProductLazy evaluates poly x Gadget -> RLWE where
//
// ct = [<decomp(cx), gadget[0]>, <decomp(cx), gadget[1]>] mod QP
// ctQP = [<decomp(cx), gadget[0]>, <decomp(cx), gadget[1]>] mod QP
//
// Expects the flag IsNTT of ct to correctly reflect the domain of cx.
// Expects the flag IsNTT of ctQP to correctly reflect the domain of cx.
//
// Result NTT domain is returned according to the NTT flag of ct.
func (eval Evaluator) GadgetProductLazy(levelQ int, cx ring.Poly, gadgetCt *GadgetCiphertext, ct *Element[ringqp.Poly]) {
// Result NTT domain is returned according to the NTT flag of ctQP.
//
// The method will return an error if ctQP.Level() < gadgetCt.Level().
func (eval Evaluator) GadgetProductLazy(levelQ int, cx ring.Poly, gadgetCt *GadgetCiphertext, ctQP *Element[ringqp.Poly]) (err error) {

if ctQP.LevelP() < gadgetCt.LevelP() {
return fmt.Errorf("ctQP.LevelP()=%d < gadgetCt.LevelP()=%d", ctQP.Level(), gadgetCt.LevelP())
}

if gadgetCt.LevelP() > 0 {
eval.gadgetProductMultiplePLazy(levelQ, cx, gadgetCt, ct)
eval.gadgetProductMultiplePLazy(levelQ, cx, gadgetCt, ctQP)
} else {
eval.gadgetProductSinglePAndBitDecompLazy(levelQ, cx, gadgetCt, ct)
eval.gadgetProductSinglePAndBitDecompLazy(levelQ, cx, gadgetCt, ctQP)
}

if !ct.IsNTT {
if !ctQP.IsNTT {
ringQP := eval.params.RingQP().AtLevel(levelQ, gadgetCt.LevelP())
ringQP.INTT(ct.Value[0], ct.Value[0])
ringQP.INTT(ct.Value[1], ct.Value[1])
ringQP.INTT(ctQP.Value[0], ctQP.Value[0])
ringQP.INTT(ctQP.Value[1], ctQP.Value[1])
}

return
}

func (eval Evaluator) gadgetProductMultiplePLazy(levelQ int, cx ring.Poly, gadgetCt *GadgetCiphertext, ct *Element[ringqp.Poly]) {
func (eval Evaluator) gadgetProductMultiplePLazy(levelQ int, cx ring.Poly, gadgetCt *GadgetCiphertext, ctQP *Element[ringqp.Poly]) {

levelP := gadgetCt.LevelP()

Expand All @@ -121,7 +132,7 @@ func (eval Evaluator) gadgetProductMultiplePLazy(levelQ int, cx ring.Poly, gadge
c2QP := eval.BuffDecompQP[0]

var cxNTT, cxInvNTT ring.Poly
if ct.IsNTT {
if ctQP.IsNTT {
cxNTT = cx
cxInvNTT = eval.BuffInvNTT
ringQ.INTT(cxNTT, cxInvNTT)
Expand All @@ -145,38 +156,38 @@ func (eval Evaluator) gadgetProductMultiplePLazy(levelQ int, cx ring.Poly, gadge
eval.DecomposeSingleNTT(levelQ, levelP, levelP+1, i, cxNTT, cxInvNTT, c2QP.Q, c2QP.P)

if i == 0 {
ringQP.MulCoeffsMontgomeryLazy(el[i][0][0], c2QP, ct.Value[0])
ringQP.MulCoeffsMontgomeryLazy(el[i][0][1], c2QP, ct.Value[1])
ringQP.MulCoeffsMontgomeryLazy(el[i][0][0], c2QP, ctQP.Value[0])
ringQP.MulCoeffsMontgomeryLazy(el[i][0][1], c2QP, ctQP.Value[1])
} else {
ringQP.MulCoeffsMontgomeryLazyThenAddLazy(el[i][0][0], c2QP, ct.Value[0])
ringQP.MulCoeffsMontgomeryLazyThenAddLazy(el[i][0][1], c2QP, ct.Value[1])
ringQP.MulCoeffsMontgomeryLazyThenAddLazy(el[i][0][0], c2QP, ctQP.Value[0])
ringQP.MulCoeffsMontgomeryLazyThenAddLazy(el[i][0][1], c2QP, ctQP.Value[1])
}

if reduce%QiOverF == QiOverF-1 {
ringQ.Reduce(ct.Value[0].Q, ct.Value[0].Q)
ringQ.Reduce(ct.Value[1].Q, ct.Value[1].Q)
ringQ.Reduce(ctQP.Value[0].Q, ctQP.Value[0].Q)
ringQ.Reduce(ctQP.Value[1].Q, ctQP.Value[1].Q)
}

if reduce%PiOverF == PiOverF-1 {
ringP.Reduce(ct.Value[0].P, ct.Value[0].P)
ringP.Reduce(ct.Value[1].P, ct.Value[1].P)
ringP.Reduce(ctQP.Value[0].P, ctQP.Value[0].P)
ringP.Reduce(ctQP.Value[1].P, ctQP.Value[1].P)
}

reduce++
}

if reduce%QiOverF != 0 {
ringQ.Reduce(ct.Value[0].Q, ct.Value[0].Q)
ringQ.Reduce(ct.Value[1].Q, ct.Value[1].Q)
ringQ.Reduce(ctQP.Value[0].Q, ctQP.Value[0].Q)
ringQ.Reduce(ctQP.Value[1].Q, ctQP.Value[1].Q)
}

if reduce%PiOverF != 0 {
ringP.Reduce(ct.Value[0].P, ct.Value[0].P)
ringP.Reduce(ct.Value[1].P, ct.Value[1].P)
ringP.Reduce(ctQP.Value[0].P, ctQP.Value[0].P)
ringP.Reduce(ctQP.Value[1].P, ctQP.Value[1].P)
}
}

func (eval Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx ring.Poly, gadgetCt *GadgetCiphertext, ct *Element[ringqp.Poly]) {
func (eval Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx ring.Poly, gadgetCt *GadgetCiphertext, ctQP *Element[ringqp.Poly]) {

levelP := gadgetCt.LevelP()

Expand All @@ -186,7 +197,7 @@ func (eval Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx ring.P
ringP := ringQP.RingP

var cxInvNTT ring.Poly
if ct.IsNTT {
if ctQP.IsNTT {
cxInvNTT = eval.BuffInvNTT
ringQ.INTT(cx, cxInvNTT)
} else {
Expand Down Expand Up @@ -232,8 +243,8 @@ func (eval Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx ring.P
ring.MaskVec(cxInvNTT.Coeffs[i], j*pw2, mask, cw)
s.NTTLazy(cw, cwNTT)
}
s.MulCoeffsMontgomeryLazy(el[i][j][0].Q.Coeffs[u], cwNTT, ct.Value[0].Q.Coeffs[u])
s.MulCoeffsMontgomeryLazy(el[i][j][1].Q.Coeffs[u], cwNTT, ct.Value[1].Q.Coeffs[u])
s.MulCoeffsMontgomeryLazy(el[i][j][0].Q.Coeffs[u], cwNTT, ctQP.Value[0].Q.Coeffs[u])
s.MulCoeffsMontgomeryLazy(el[i][j][1].Q.Coeffs[u], cwNTT, ctQP.Value[1].Q.Coeffs[u])
}

if ringP != nil {
Expand All @@ -244,8 +255,8 @@ func (eval Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx ring.P
ring.MaskVec(cxInvNTT.Coeffs[i], j*pw2, mask, cw)
s.NTTLazy(cw, cwNTT)
}
s.MulCoeffsMontgomeryLazy(el[i][j][0].P.Coeffs[u], cwNTT, ct.Value[0].P.Coeffs[u])
s.MulCoeffsMontgomeryLazy(el[i][j][1].P.Coeffs[u], cwNTT, ct.Value[1].P.Coeffs[u])
s.MulCoeffsMontgomeryLazy(el[i][j][0].P.Coeffs[u], cwNTT, ctQP.Value[0].P.Coeffs[u])
s.MulCoeffsMontgomeryLazy(el[i][j][1].P.Coeffs[u], cwNTT, ctQP.Value[1].P.Coeffs[u])
}
}

Expand All @@ -257,8 +268,8 @@ func (eval Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx ring.P
ring.MaskVec(cxInvNTT.Coeffs[i], j*pw2, mask, cw)
s.NTTLazy(cw, cwNTT)
}
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][0].Q.Coeffs[u], cwNTT, ct.Value[0].Q.Coeffs[u])
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][1].Q.Coeffs[u], cwNTT, ct.Value[1].Q.Coeffs[u])
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][0].Q.Coeffs[u], cwNTT, ctQP.Value[0].Q.Coeffs[u])
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][1].Q.Coeffs[u], cwNTT, ctQP.Value[1].Q.Coeffs[u])
}

if ringP != nil {
Expand All @@ -269,35 +280,35 @@ func (eval Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx ring.P
ring.MaskVec(cxInvNTT.Coeffs[i], j*pw2, mask, cw)
s.NTTLazy(cw, cwNTT)
}
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][0].P.Coeffs[u], cwNTT, ct.Value[0].P.Coeffs[u])
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][1].P.Coeffs[u], cwNTT, ct.Value[1].P.Coeffs[u])
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][0].P.Coeffs[u], cwNTT, ctQP.Value[0].P.Coeffs[u])
s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][1].P.Coeffs[u], cwNTT, ctQP.Value[1].P.Coeffs[u])
}
}
}

if reduce%QiOverF == QiOverF-1 {
ringQ.Reduce(ct.Value[0].Q, ct.Value[0].Q)
ringQ.Reduce(ct.Value[1].Q, ct.Value[1].Q)
ringQ.Reduce(ctQP.Value[0].Q, ctQP.Value[0].Q)
ringQ.Reduce(ctQP.Value[1].Q, ctQP.Value[1].Q)
}

if reduce%PiOverF == PiOverF-1 {
ringP.Reduce(ct.Value[0].P, ct.Value[0].P)
ringP.Reduce(ct.Value[1].P, ct.Value[1].P)
ringP.Reduce(ctQP.Value[0].P, ctQP.Value[0].P)
ringP.Reduce(ctQP.Value[1].P, ctQP.Value[1].P)
}

reduce++
}
}

if reduce%QiOverF != 0 {
ringQ.Reduce(ct.Value[0].Q, ct.Value[0].Q)
ringQ.Reduce(ct.Value[1].Q, ct.Value[1].Q)
ringQ.Reduce(ctQP.Value[0].Q, ctQP.Value[0].Q)
ringQ.Reduce(ctQP.Value[1].Q, ctQP.Value[1].Q)
}

if ringP != nil {
if reduce%PiOverF != 0 {
ringP.Reduce(ct.Value[0].P, ct.Value[0].P)
ringP.Reduce(ct.Value[1].P, ct.Value[1].P)
ringP.Reduce(ctQP.Value[0].P, ctQP.Value[0].P)
ringP.Reduce(ctQP.Value[1].P, ctQP.Value[1].P)
}
}
}
Expand All @@ -319,7 +330,9 @@ func (eval Evaluator) GadgetProductHoisted(levelQ int, BuffQPDecompQP []ringqp.P
}
ctQP.MetaData = ct.MetaData

eval.GadgetProductHoistedLazy(levelQ, BuffQPDecompQP, gadgetCt, ctQP)
if err := eval.GadgetProductHoistedLazy(levelQ, BuffQPDecompQP, gadgetCt, ctQP); err != nil {
panic(fmt.Errorf("GadgetProductHoistedLazy: %w", err))
}
eval.ModDown(levelQ, gadgetCt.LevelP(), ctQP, ct)
}

Expand All @@ -330,21 +343,29 @@ func (eval Evaluator) GadgetProductHoisted(levelQ int, BuffQPDecompQP []ringqp.P
//
// BuffQPDecompQP is expected to be in the NTT domain.
//
// Result NTT domain is returned according to the NTT flag of ct.
func (eval Evaluator) GadgetProductHoistedLazy(levelQ int, BuffQPDecompQP []ringqp.Poly, gadgetCt *GadgetCiphertext, ct *Element[ringqp.Poly]) {
// Result NTT domain is returned according to the NTT flag of ctQP.
//
// The method will return an error if ctQP.Level() < gadgetCt.Level().
func (eval Evaluator) GadgetProductHoistedLazy(levelQ int, BuffQPDecompQP []ringqp.Poly, gadgetCt *GadgetCiphertext, ctQP *Element[ringqp.Poly]) (err error) {

// Sanity check for invalid parameters.
if gadgetCt.BaseTwoDecomposition != 0 {
panic(fmt.Errorf("cannot GadgetProductHoistedLazy: method is unsupported for BaseTwoDecomposition != 0"))
return fmt.Errorf("method is unsupported for BaseTwoDecomposition != 0")
}

eval.gadgetProductMultiplePLazyHoisted(levelQ, BuffQPDecompQP, gadgetCt, ct)
if ctQP.LevelP() < gadgetCt.LevelP() {
return fmt.Errorf("ctQP.LevelP()=%d < gadgetCt.LevelP()=%d", ctQP.Level(), gadgetCt.LevelP())
}

eval.gadgetProductMultiplePLazyHoisted(levelQ, BuffQPDecompQP, gadgetCt, ctQP)

if !ct.IsNTT {
if !ctQP.IsNTT {
ringQP := eval.params.RingQP().AtLevel(levelQ, gadgetCt.LevelP())
ringQP.INTT(ct.Value[0], ct.Value[0])
ringQP.INTT(ct.Value[1], ct.Value[1])
ringQP.INTT(ctQP.Value[0], ctQP.Value[0])
ringQP.INTT(ctQP.Value[1], ctQP.Value[1])
}

return
}

func (eval Evaluator) gadgetProductMultiplePLazyHoisted(levelQ int, BuffQPDecompQP []ringqp.Poly, gadgetCt *GadgetCiphertext, ct *Element[ringqp.Poly]) {
Expand Down
2 changes: 1 addition & 1 deletion core/rlwe/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func NTTSparseAndMontgomery(r *ring.Ring, metadata *MetaData, pol ring.Poly) {
} else {

var n int
var NTT func(p1, p2 []uint64, N int, Q, QInv uint64, BRedConstant, nttPsi []uint64)
var NTT func(p1, p2 []uint64, N int, Q, QInv uint64, BRedConstant [2]uint64, nttPsi []uint64)
switch r.Type() {
case ring.Standard:
n = 2 << metadata.LogDimensions.Cols
Expand Down
Loading

0 comments on commit a4e50f6

Please sign in to comment.