mirror of
https://github.com/luxfi/lattice.git
synced 2026-09-24 00:25:44 +00:00
- Update module path to github.com/luxfi/lattice/v7 - Add GPU acceleration with Metal (macOS) and CUDA backends - Enhance threshold FHE operations - Revert to vanilla Apache 2.0 license (remove tfhe) - Update Go version to 1.25.5 - Merge upstream lattigo improvements
166 lines
5.2 KiB
Go
166 lines
5.2 KiB
Go
package rlwe
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/luxfi/lattice/v7/ring/ringqp"
|
|
"github.com/luxfi/lattice/v7/utils"
|
|
)
|
|
|
|
// Automorphism computes phi(ct), where phi is the map X -> X^galEl. The method requires
|
|
// that the corresponding RotationKey has been added to the [Evaluator]. The method will
|
|
// return an error if either ctIn or opOut degree is not equal to 1.
|
|
func (eval Evaluator) Automorphism(ctIn *Ciphertext, galEl uint64, opOut *Ciphertext) (err error) {
|
|
|
|
if ctIn.Degree() != 1 || opOut.Degree() != 1 {
|
|
return fmt.Errorf("cannot apply Automorphism: input and output Ciphertext must be of degree 1")
|
|
}
|
|
|
|
if galEl == 1 {
|
|
if opOut != ctIn {
|
|
opOut.Copy(ctIn)
|
|
}
|
|
return
|
|
}
|
|
|
|
var evk *GaloisKey
|
|
if evk, err = eval.CheckAndGetGaloisKey(galEl); err != nil {
|
|
return fmt.Errorf("cannot apply Automorphism: %w", err)
|
|
}
|
|
|
|
level := utils.Min(ctIn.Level(), opOut.Level())
|
|
|
|
opOut.Resize(opOut.Degree(), level)
|
|
|
|
ringQ := eval.params.RingQ().AtLevel(level)
|
|
|
|
ctTmp := eval.pool.GetBuffCt(1, ringQ.Level())
|
|
defer eval.pool.RecycleBuffCt(ctTmp)
|
|
ctTmp.MetaData = ctIn.MetaData
|
|
|
|
eval.GadgetProduct(level, ctIn.Value[1], &evk.GadgetCiphertext, ctTmp)
|
|
|
|
ringQ.Add(ctTmp.Value[0], ctIn.Value[0], ctTmp.Value[0])
|
|
|
|
if ctIn.IsNTT {
|
|
ringQ.AutomorphismNTTWithIndex(ctTmp.Value[0], eval.automorphismIndex[galEl], opOut.Value[0])
|
|
ringQ.AutomorphismNTTWithIndex(ctTmp.Value[1], eval.automorphismIndex[galEl], opOut.Value[1])
|
|
} else {
|
|
ringQ.Automorphism(ctTmp.Value[0], galEl, opOut.Value[0])
|
|
ringQ.Automorphism(ctTmp.Value[1], galEl, opOut.Value[1])
|
|
}
|
|
|
|
*opOut.MetaData = *ctIn.MetaData
|
|
|
|
return
|
|
}
|
|
|
|
// AutomorphismHoisted is similar to [Evaluator.Automorphism], except that it takes as input ctIn and c1DecompQP, where c1DecompQP is the RNS
|
|
// decomposition of its element of degree 1. This decomposition can be obtained with [Evaluator.DecomposeNTT].
|
|
// The method requires that the corresponding RotationKey has been added to the [Evaluator].
|
|
// The method will return an error if either ctIn or opOut degree is not equal to 1.
|
|
func (eval Evaluator) AutomorphismHoisted(level int, ctIn *Ciphertext, c1DecompQP []ringqp.Poly, galEl uint64, opOut *Ciphertext) (err error) {
|
|
|
|
if ctIn.Degree() != 1 || opOut.Degree() != 1 {
|
|
return fmt.Errorf("cannot apply AutomorphismHoisted: input and output Ciphertext must be of degree 1")
|
|
}
|
|
|
|
if galEl == 1 {
|
|
if ctIn != opOut {
|
|
opOut.Copy(ctIn)
|
|
}
|
|
return
|
|
}
|
|
|
|
var evk *GaloisKey
|
|
if evk, err = eval.CheckAndGetGaloisKey(galEl); err != nil {
|
|
return fmt.Errorf("cannot apply AutomorphismHoisted: %w", err)
|
|
}
|
|
|
|
opOut.Resize(opOut.Degree(), level)
|
|
|
|
ringQ := eval.params.RingQ().AtLevel(level)
|
|
|
|
ctTmp := eval.pool.GetBuffCt(1, ringQ.Level())
|
|
defer eval.pool.RecycleBuffCt(ctTmp)
|
|
ctTmp.MetaData = ctIn.MetaData
|
|
|
|
eval.GadgetProductHoisted(level, c1DecompQP, &evk.EvaluationKey.GadgetCiphertext, ctTmp)
|
|
ringQ.Add(ctTmp.Value[0], ctIn.Value[0], ctTmp.Value[0])
|
|
|
|
if ctIn.IsNTT {
|
|
ringQ.AutomorphismNTTWithIndex(ctTmp.Value[0], eval.automorphismIndex[galEl], opOut.Value[0])
|
|
ringQ.AutomorphismNTTWithIndex(ctTmp.Value[1], eval.automorphismIndex[galEl], opOut.Value[1])
|
|
} else {
|
|
ringQ.Automorphism(ctTmp.Value[0], galEl, opOut.Value[0])
|
|
ringQ.Automorphism(ctTmp.Value[1], galEl, opOut.Value[1])
|
|
}
|
|
|
|
*opOut.MetaData = *ctIn.MetaData
|
|
|
|
return
|
|
}
|
|
|
|
// AutomorphismHoistedLazy is similar to [Evaluator.AutomorphismHoisted], except that it returns a ciphertext modulo QP and scaled by P.
|
|
// The method requires that the corresponding RotationKey has been added to the [Evaluator].
|
|
// Result NTT domain is returned according to the NTT flag of ctQP.
|
|
func (eval Evaluator) AutomorphismHoistedLazy(levelQ int, ctIn *Ciphertext, c1DecompQP []ringqp.Poly, galEl uint64, ctQP *Element[ringqp.Poly]) (err error) {
|
|
|
|
var evk *GaloisKey
|
|
if evk, err = eval.CheckAndGetGaloisKey(galEl); err != nil {
|
|
return fmt.Errorf("cannot apply AutomorphismHoistedLazy: %w", err)
|
|
}
|
|
|
|
levelP := evk.LevelP()
|
|
|
|
if ctQP.LevelP() < levelP {
|
|
return fmt.Errorf("ctQP.LevelP()=%d < GaloisKey[%d].LevelP()=%d", ctQP.LevelP(), galEl, levelP)
|
|
}
|
|
|
|
ringQP := eval.params.RingQP().AtLevel(levelQ, levelP)
|
|
poolQP := eval.pool.AtLevel(levelQ, levelP)
|
|
buffQP1 := poolQP.GetBuffPolyQP()
|
|
defer poolQP.RecycleBuffPolyQP(buffQP1)
|
|
buffQP2 := poolQP.GetBuffPolyQP()
|
|
defer poolQP.RecycleBuffPolyQP(buffQP2)
|
|
|
|
ctTmp := &Element[ringqp.Poly]{}
|
|
ctTmp.Value = []ringqp.Poly{*buffQP1, *buffQP2}
|
|
ctTmp.MetaData = ctIn.MetaData
|
|
|
|
if err = eval.GadgetProductHoistedLazy(levelQ, c1DecompQP, &evk.GadgetCiphertext, ctTmp); err != nil {
|
|
panic(fmt.Errorf("eval.GadgetProductHoistedLazy: %w", err))
|
|
}
|
|
|
|
ringQ := ringQP.RingQ
|
|
ringP := ringQP.RingP
|
|
|
|
index := eval.automorphismIndex[galEl]
|
|
|
|
if ctQP.IsNTT {
|
|
|
|
ringQP.AutomorphismNTTWithIndex(ctTmp.Value[1], index, ctQP.Value[1])
|
|
|
|
if levelP > -1 {
|
|
ringQ.MulScalarBigint(ctIn.Value[0], ringP.ModulusAtLevel[levelP], ctTmp.Value[1].Q)
|
|
}
|
|
|
|
ringQ.Add(ctTmp.Value[0].Q, ctTmp.Value[1].Q, ctTmp.Value[0].Q)
|
|
|
|
ringQP.AutomorphismNTTWithIndex(ctTmp.Value[0], index, ctQP.Value[0])
|
|
} else {
|
|
|
|
ringQP.Automorphism(ctTmp.Value[1], galEl, ctQP.Value[1])
|
|
|
|
if levelP > -1 {
|
|
ringQ.MulScalarBigint(ctIn.Value[0], ringP.ModulusAtLevel[levelP], ctTmp.Value[1].Q)
|
|
}
|
|
|
|
ringQ.Add(ctTmp.Value[0].Q, ctTmp.Value[1].Q, ctTmp.Value[0].Q)
|
|
|
|
ringQP.Automorphism(ctTmp.Value[0], galEl, ctQP.Value[0])
|
|
}
|
|
|
|
return
|
|
}
|