Added evaluator and encryptor contexts for ckks

This commit is contained in:
Christian Grigis
2019-11-27 16:19:15 +01:00
parent 5c09d4cfd9
commit 3de057efbe
18 changed files with 517 additions and 237 deletions

View File

@@ -3,8 +3,6 @@ package bfv
import (
"fmt"
"testing"
"github.com/ldsec/lattigo/ring"
)
func Benchmark_BFV(b *testing.B) {
@@ -40,12 +38,7 @@ func Benchmark_BFV(b *testing.B) {
pt := bfvContext.NewPlaintext()
pt.setCoefficientsUint64(bfvContext, ptcoeffs)
ringCtx := ring.NewContext()
ringCtx.SetParameters(params.N, params.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := NewCiphertextRingContext(&params)
ctd1 := NewCiphertext(1, ringCtx)
b.Run(testString("EncryptFromPk", &params), func(b *testing.B) {

View File

@@ -426,12 +426,7 @@ func testEvaluatorSub(t *testing.T) {
func testEvaluatorMul(t *testing.T) {
for _, parameters := range testParams.bfvParameters {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := NewCiphertextRingContext(parameters)
params := genBfvParams(parameters)
@@ -543,12 +538,7 @@ func testRotateRows(t *testing.T) {
func testRotateCols(t *testing.T) {
for _, parameters := range testParams.bfvParameters {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := NewCiphertextRingContext(parameters)
params := genBfvParams(parameters)

View File

@@ -14,6 +14,18 @@ func NewCiphertextStruct() (ciphertext *Ciphertext) {
return &Ciphertext{&bfvElement{}}
}
func NewCiphertextRingContext(params *Parameters) *ring.Context {
ringCtx := ring.NewContext()
ringCtx.SetParameters(params.N, params.Qi)
err := ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
return ringCtx
}
// NewCiphertext creates a new empty ciphertext of degree degree.
func NewCiphertext(degree uint64, ringCtx *ring.Context) *Ciphertext {
ciphertext := &Ciphertext{&bfvElement{}}

View File

@@ -19,13 +19,13 @@ func (evaluator *Evaluator) PowerOf2(el0 *Ciphertext, logPow2 uint64, evakey *Ev
evaluator.MulRelin(el0.Element(), el0.Element(), evakey, elOut)
evaluator.Rescale(elOut, evaluator.ckkscontext.scale, elOut)
evaluator.Rescale(elOut, evaluator.context.scale, elOut)
for i := uint64(1); i < logPow2; i++ {
evaluator.MulRelin(elOut.Element(), elOut.Element(), evakey, elOut)
evaluator.Rescale(elOut, evaluator.ckkscontext.scale, elOut)
evaluator.Rescale(elOut, evaluator.context.scale, elOut)
}
}
}
@@ -33,7 +33,7 @@ func (evaluator *Evaluator) PowerOf2(el0 *Ciphertext, logPow2 uint64, evakey *Ev
// PowerNew compute ct0^degree, consuming log(degree) levels, and returns the result on a new element. Providing an evaluation
// key is necessary when degree > 2.
func (evaluator *Evaluator) PowerNew(op *Ciphertext, degree uint64, evakey *EvaluationKey) (opOut *Ciphertext) {
opOut = evaluator.ckkscontext.NewCiphertext(1, op.Level(), op.Scale())
opOut = NewCiphertext(1, op.Level(), op.Scale(), evaluator.context.contextQ)
evaluator.Power(op, degree, evakey, opOut)
return
}
@@ -58,13 +58,13 @@ func (evaluator *Evaluator) Power(ct0 *Ciphertext, degree uint64, evakey *Evalua
logDegree = uint64(bits.Len64(degree)) - 1
po2Degree = 1 << logDegree
tmp := evaluator.ckkscontext.NewCiphertext(1, tmpct0.Level(), tmpct0.Scale())
tmp := NewCiphertext(1, tmpct0.Level(), tmpct0.Scale(), evaluator.context.contextQ)
evaluator.PowerOf2(tmpct0.Ciphertext(), logDegree, evakey, tmp)
evaluator.MulRelin(res.Element(), tmp.Element(), evakey, res)
evaluator.Rescale(res, evaluator.ckkscontext.scale, res)
evaluator.Rescale(res, evaluator.context.scale, res)
degree -= po2Degree
}
@@ -85,13 +85,13 @@ func (evaluator *Evaluator) InverseNew(ct0 *Ciphertext, steps uint64, evakey *Ev
evaluator.MulRelin(cbar.Element(), cbar.Element(), evakey, cbar.Ciphertext())
evaluator.Rescale(cbar, evaluator.ckkscontext.scale, cbar)
evaluator.Rescale(cbar, evaluator.context.scale, cbar)
tmp = evaluator.AddConstNew(cbar, 1)
evaluator.MulRelin(tmp.Element(), res.Element(), evakey, tmp.Ciphertext())
evaluator.Rescale(tmp, evaluator.ckkscontext.scale, tmp)
evaluator.Rescale(tmp, evaluator.context.scale, tmp)
res = tmp.CopyNew().Ciphertext()
}

View File

@@ -15,7 +15,7 @@ func (evaluator *Evaluator) EvaluateChebyFast(ct *Ciphertext, cheby *ChebyshevIn
evaluator.MultByConst(C[1], 2/(cheby.b-cheby.a), C[1])
evaluator.AddConst(C[1], (-cheby.a-cheby.b)/(cheby.b-cheby.a), C[1])
evaluator.Rescale(C[1], evaluator.ckkscontext.scale, C[1])
evaluator.Rescale(C[1], evaluator.context.scale, C[1])
M := uint64(bits.Len64(cheby.degree - 1))
L := uint64(M >> 1)
@@ -86,7 +86,7 @@ func computePowerBasisCheby(n uint64, C map[uint64]*Ciphertext, evaluator *Evalu
// Computes C[n] = C[a]*C[b]
C[n] = evaluator.MulRelinNew(C[a], C[b], evakey)
evaluator.Rescale(C[n], evaluator.ckkscontext.scale, C[n])
evaluator.Rescale(C[n], evaluator.context.scale, C[n])
// Computes C[n] = 2*C[a]*C[b]
evaluator.Add(C[n], C[n], C[n])
@@ -123,7 +123,7 @@ func recurseCheby(maxDegree, L, M uint64, coeffs map[uint64]complex128, C map[ui
evaluator.Add(res, tmp, res)
evaluator.Rescale(res, evaluator.ckkscontext.scale, res)
evaluator.Rescale(res, evaluator.context.scale, res)
return res

View File

@@ -9,18 +9,66 @@ type Ciphertext struct {
*ckksElement
}
// NewCiphertext returns a new Ciphertext element.
func NewCiphertext() (ciphertext *Ciphertext) {
// NewCiphertextStruct returns a new Ciphertext element.
func NewCiphertextStruct() (ciphertext *Ciphertext) {
return &Ciphertext{&ckksElement{}}
}
func NewCiphertextRingContext(params *Parameters) *ring.Context {
scalechain := make([]float64, len(params.Modulichain))
// Extracts all the different primes bit size and maps their number
primesbitlen := make(map[uint64]uint64)
for i, qi := range params.Modulichain {
primesbitlen[uint64(qi)]++
if uint64(params.Modulichain[i]) > 60 {
panic("provided moduli must be smaller than 61")
}
}
for _, pj := range params.P {
primesbitlen[uint64(pj)]++
if uint64(pj) > 60 {
panic("provided P must be smaller than 61")
}
}
// For each bitsize, finds that many primes
primes := make(map[uint64][]uint64)
for key, value := range primesbitlen {
primes[key] = GenerateCKKSPrimes(key, uint64(params.LogN), value)
}
// Assigns the primes to the ckks moduli chain
moduli := make([]uint64, len(params.Modulichain))
for i, qi := range params.Modulichain {
moduli[i] = primes[uint64(params.Modulichain[i])][0]
primes[uint64(qi)] = primes[uint64(qi)][1:]
scalechain[i] = float64(moduli[i])
}
ringCtx := ring.NewContext()
ringCtx.SetParameters(1<<params.LogN, moduli)
err := ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
return ringCtx
}
// NewCiphertext creates a new ciphertext parameterized by degree, level and scale.
func (ckkscontext *Context) NewCiphertext(degree uint64, level uint64, scale float64) *Ciphertext {
func NewCiphertext(degree uint64, level uint64, scale float64, ringCtx *ring.Context) *Ciphertext {
ciphertext := &Ciphertext{&ckksElement{}}
ciphertext.value = make([]*ring.Poly, degree+1)
for i := uint64(0); i < degree+1; i++ {
ciphertext.value[i] = ckkscontext.contextQ.NewPolyLvl(level)
ciphertext.value[i] = ringCtx.NewPolyLvl(level)
}
ciphertext.scale = scale

View File

@@ -97,11 +97,11 @@ func genCkksParams(contextParameters *Parameters) (params *ckksParams) {
params.encoder = NewEncoder(contextParameters)
params.encryptorPk = params.ckkscontext.NewEncryptorFromPk(params.pk)
params.encryptorSk = params.ckkscontext.NewEncryptorFromSk(params.sk)
params.encryptorPk = NewEncryptorFromPk(params.pk, contextParameters)
params.encryptorSk = NewEncryptorFromSk(params.sk, contextParameters)
params.decryptor = params.ckkscontext.NewDecryptor(params.sk)
params.evaluator = params.ckkscontext.NewEvaluator()
params.evaluator = NewEvaluator(contextParameters)
return
@@ -871,12 +871,14 @@ func testRotateColumns(t *testing.T) {
rotKey := params.kgen.NewRotationKeysPow2(params.sk)
ringCtx := NewCiphertextRingContext(parameters)
t.Run(testString("InPlace/", params), func(t *testing.T) {
values1, _, ciphertext1 := newTestVectorsReals(params, params.encryptorSk, -1, 1, t)
values2 := make([]complex128, len(values1))
ciphertext2 := params.ckkscontext.NewCiphertext(ciphertext1.Degree(), ciphertext1.Level(), ciphertext1.Scale())
ciphertext2 := NewCiphertext(ciphertext1.Degree(), ciphertext1.Level(), ciphertext1.Scale(), ringCtx)
for n := 1; n < len(values1); n <<= 1 {
@@ -897,7 +899,7 @@ func testRotateColumns(t *testing.T) {
values1, _, ciphertext1 := newTestVectorsReals(params, params.encryptorSk, -1, 1, t)
values2 := make([]complex128, len(values1))
ciphertext2 := params.ckkscontext.NewCiphertext(ciphertext1.Degree(), ciphertext1.Level(), ciphertext1.Scale())
ciphertext2 := NewCiphertext(ciphertext1.Degree(), ciphertext1.Level(), ciphertext1.Scale(), ringCtx)
for n := 1; n < len(values1); n <<= 1 {
@@ -918,7 +920,7 @@ func testRotateColumns(t *testing.T) {
values1, _, ciphertext1 := newTestVectorsReals(params, params.encryptorSk, -1, 1, t)
values2 := make([]complex128, len(values1))
ciphertext2 := params.ckkscontext.NewCiphertext(ciphertext1.Degree(), ciphertext1.Level(), ciphertext1.Scale())
ciphertext2 := NewCiphertext(ciphertext1.Degree(), ciphertext1.Level(), ciphertext1.Scale(), ringCtx)
for n := 1; n < 4; n++ {
@@ -953,7 +955,7 @@ func testMarshaller(t *testing.T) {
marshalledCiphertext, err := ciphertextWant.MarshalBinary()
check(t, err)
ciphertextTest := NewCiphertext()
ciphertextTest := NewCiphertextStruct()
err = ciphertextTest.UnmarshalBinary(marshalledCiphertext)
check(t, err)

View File

@@ -4,12 +4,140 @@ import (
"github.com/ldsec/lattigo/ring"
)
type encryptorContext struct {
// Context parameters
n uint64
// Number of available levels
levels uint64
// Contexts
specialPrimes []uint64
contextQ *ring.Context
contextP *ring.Context
contextKeys *ring.Context
// Pre-computed values for the rescaling
rescaleParamsKeys []uint64 // (P^-1) mod each qi
// Samplers
gaussianSampler *ring.KYSampler
}
func newEncryptorContext(params *Parameters) *encryptorContext {
n := uint64(1 << uint64(params.LogN))
levels := uint64(len(params.Modulichain))
scalechain := make([]float64, len(params.Modulichain))
// Extracts all the different primes bit size and maps their number
primesbitlen := make(map[uint64]uint64)
for i, qi := range params.Modulichain {
primesbitlen[uint64(qi)]++
if uint64(params.Modulichain[i]) > 60 {
panic("provided moduli must be smaller than 61")
}
}
for _, pj := range params.P {
primesbitlen[uint64(pj)]++
if uint64(pj) > 60 {
panic("provided P must be smaller than 61")
}
}
// For each bitsize, finds that many primes
primes := make(map[uint64][]uint64)
for key, value := range primesbitlen {
primes[key] = GenerateCKKSPrimes(key, uint64(params.LogN), value)
}
// Assigns the primes to the ckks moduli chain
moduli := make([]uint64, len(params.Modulichain))
for i, qi := range params.Modulichain {
moduli[i] = primes[uint64(params.Modulichain[i])][0]
primes[uint64(qi)] = primes[uint64(qi)][1:]
scalechain[i] = float64(moduli[i])
}
// Assigns the primes to the special primes list for the the keyscontext
specialPrimes := make([]uint64, len(params.P))
for i, pj := range params.P {
specialPrimes[i] = primes[uint64(pj)][0]
primes[uint64(pj)] = primes[uint64(pj)][1:]
}
// Contexts
contextQ := ring.NewContext()
contextQ.SetParameters(1<<params.LogN, moduli)
err := contextQ.GenNTTParams()
if err != nil {
panic(err)
}
contextP := ring.NewContext()
contextP.SetParameters(1<<params.LogN, specialPrimes)
err = contextP.GenNTTParams()
if err != nil {
panic(err)
}
contextKeys := ring.NewContext()
contextKeys.SetParameters(1<<params.LogN, append(moduli, specialPrimes...))
err = contextKeys.GenNTTParams()
if err != nil {
panic(err)
}
var Qi uint64
bredParams := contextQ.GetBredParams()
rescaleParamsKeys := make([]uint64, levels)
PBig := ring.NewUint(1)
for _, pj := range specialPrimes {
PBig.Mul(PBig, ring.NewUint(pj))
}
tmp := ring.NewUint(0)
for i := uint64(0); i < levels; i++ {
Qi = moduli[i]
tmp.Mod(PBig, ring.NewUint(Qi))
rescaleParamsKeys[i] = ring.MForm(ring.ModExp(ring.BRedAdd(tmp.Uint64(), Qi, bredParams[i]), Qi-2, Qi), Qi, bredParams[i])
}
gaussianSampler := contextKeys.NewKYSampler(params.Sigma, int(6*params.Sigma))
return &encryptorContext{
n: n,
levels: levels,
specialPrimes: specialPrimes,
contextQ: contextQ,
contextP: contextP,
contextKeys: contextKeys,
rescaleParamsKeys: rescaleParamsKeys,
gaussianSampler: gaussianSampler,
}
}
// Encryptor is a struct used to encrypt plaintext and storing the public-key and/or secret-key.
type Encryptor struct {
ckkscontext *Context
pk *PublicKey
sk *SecretKey
polypool [3]*ring.Poly
context *encryptorContext
pk *PublicKey
sk *SecretKey
polypool [3]*ring.Poly
rescalepool []uint64
@@ -18,40 +146,41 @@ type Encryptor struct {
// NewEncryptorFromPk creates a new Encryptor with the provided public-key.
// This encryptor can be used to encrypt plaintexts, using the stored key.
func (ckkscontext *Context) NewEncryptorFromPk(pk *PublicKey) *Encryptor {
return ckkscontext.newEncryptor(pk, nil)
func NewEncryptorFromPk(pk *PublicKey, params *Parameters) *Encryptor {
return newEncryptor(pk, nil, params)
}
// NewEncryptorFromSk creates a new Encryptor with the provided secret-key.
// This encryptor can be used to encrypt plaintexts, using the stored key.
func (ckkscontext *Context) NewEncryptorFromSk(sk *SecretKey) *Encryptor {
return ckkscontext.newEncryptor(nil, sk)
func NewEncryptorFromSk(sk *SecretKey, params *Parameters) *Encryptor {
return newEncryptor(nil, sk, params)
}
// NewEncryptor creates a new Encryptor with the input public-key and/or secret-key.
// This encryptor can be used to encrypt plaintexts, using the stored keys.
func (ckkscontext *Context) newEncryptor(pk *PublicKey, sk *SecretKey) (encryptor *Encryptor) {
func newEncryptor(pk *PublicKey, sk *SecretKey, params *Parameters) (encryptor *Encryptor) {
context := newEncryptorContext(params)
if pk != nil && (uint64(pk.pk[0].GetDegree()) != ckkscontext.n || uint64(pk.pk[1].GetDegree()) != ckkscontext.n) {
if pk != nil && (uint64(pk.pk[0].GetDegree()) != context.n || uint64(pk.pk[1].GetDegree()) != context.n) {
panic("pk ring degree doesn't match ckkscontext ring degree")
}
if sk != nil && uint64(sk.sk.GetDegree()) != ckkscontext.n {
if sk != nil && uint64(sk.sk.GetDegree()) != context.n {
panic("sk ring degree doesn't match ckkscontext ring degree")
}
encryptor = new(Encryptor)
encryptor.ckkscontext = ckkscontext
encryptor.context = context
encryptor.pk = pk
encryptor.sk = sk
encryptor.polypool[0] = ckkscontext.contextKeys.NewPoly()
encryptor.polypool[1] = ckkscontext.contextKeys.NewPoly()
encryptor.polypool[2] = ckkscontext.contextKeys.NewPoly()
encryptor.polypool[0] = context.contextKeys.NewPoly()
encryptor.polypool[1] = context.contextKeys.NewPoly()
encryptor.polypool[2] = context.contextKeys.NewPoly()
encryptor.rescalepool = make([]uint64, ckkscontext.n)
encryptor.rescalepool = make([]uint64, context.n)
encryptor.baseconverter = ring.NewFastBasisExtender(ckkscontext.contextQ.Modulus, ckkscontext.specialprimes)
encryptor.baseconverter = ring.NewFastBasisExtender(context.contextQ.Modulus, context.specialPrimes)
return encryptor
}
@@ -63,7 +192,7 @@ func (ckkscontext *Context) newEncryptor(pk *PublicKey, sk *SecretKey) (encrypto
// encrypt with sk : ciphertext = [-a*sk + m + e, a]
func (encryptor *Encryptor) EncryptNew(plaintext *Plaintext) (ciphertext *Ciphertext) {
ciphertext = encryptor.ckkscontext.NewCiphertext(1, plaintext.Level(), plaintext.Scale())
ciphertext = NewCiphertext(1, plaintext.Level(), plaintext.Scale(), encryptor.context.contextQ)
encryptor.Encrypt(plaintext, ciphertext)
return
}
@@ -75,7 +204,7 @@ func (encryptor *Encryptor) EncryptNew(plaintext *Plaintext) (ciphertext *Cipher
// encrypt with sk : ciphertext = [-a*sk + m + e, a]
func (encryptor *Encryptor) Encrypt(plaintext *Plaintext, ciphertext *Ciphertext) {
if plaintext.Level() != encryptor.ckkscontext.levels-1 {
if plaintext.Level() != encryptor.context.levels-1 {
panic("cannot encrypt -> plaintext not at maximum level")
}
@@ -97,10 +226,10 @@ func encryptfrompk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe
// We sample a R-WLE instance (encryption of zero) over the keys context (ciphertext context + special prime)
contextKeys := encryptor.ckkscontext.contextKeys
contextQ := encryptor.ckkscontext.contextQ
contextKeys := encryptor.context.contextKeys
contextQ := encryptor.context.contextQ
encryptor.ckkscontext.contextKeys.SampleTernaryMontgomeryNTT(encryptor.polypool[2], 0.5)
encryptor.context.contextKeys.SampleTernaryMontgomeryNTT(encryptor.polypool[2], 0.5)
// ct0 = u*pk0
contextKeys.MulCoeffsMontgomery(encryptor.polypool[2], encryptor.pk.pk[0], encryptor.polypool[0])
@@ -112,15 +241,15 @@ func encryptfrompk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe
contextKeys.InvNTT(encryptor.polypool[1], encryptor.polypool[1])
// ct0 = u*pk0 + e0
encryptor.ckkscontext.gaussianSampler.SampleAndAdd(encryptor.polypool[0])
encryptor.context.gaussianSampler.SampleAndAdd(encryptor.polypool[0])
// ct1 = u*pk1 + e1
encryptor.ckkscontext.gaussianSampler.SampleAndAdd(encryptor.polypool[1])
encryptor.context.gaussianSampler.SampleAndAdd(encryptor.polypool[1])
// ct0 = (u*pk0 + e0)/P
encryptor.baseconverter.ModDown(contextKeys, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2])
encryptor.baseconverter.ModDown(contextKeys, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2])
// ct1 = (u*pk1 + e1)/P
encryptor.baseconverter.ModDown(contextKeys, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2])
encryptor.baseconverter.ModDown(contextKeys, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2])
// 2*#Q NTT
contextQ.NTT(ciphertext.value[0], ciphertext.value[0])
@@ -133,9 +262,9 @@ func encryptfrompk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe
}
func encryptfromsk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphertext) {
contextKeys := encryptor.ckkscontext.contextKeys
contextP := encryptor.ckkscontext.contextP
contextQ := encryptor.ckkscontext.contextQ
contextKeys := encryptor.context.contextKeys
contextP := encryptor.context.contextP
contextQ := encryptor.context.contextQ
// ct1 = a
contextKeys.UniformPoly(encryptor.polypool[1])
@@ -148,15 +277,15 @@ func encryptfromsk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe
contextKeys.InvNTT(encryptor.polypool[0], encryptor.polypool[0])
// ct0 = -s*a + e
encryptor.ckkscontext.gaussianSampler.SampleAndAdd(encryptor.polypool[0])
encryptor.context.gaussianSampler.SampleAndAdd(encryptor.polypool[0])
// We rescal by the special prime, dividing the error by this prime
// ct0 = (-s*a + e)/P
encryptor.baseconverter.ModDown(contextKeys, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2])
encryptor.baseconverter.ModDown(contextKeys, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2])
// #Q + #P NTT
// ct1 = a/P
encryptor.baseconverter.ModDownNTT(contextQ, contextP, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2])
encryptor.baseconverter.ModDownNTT(contextQ, contextP, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2])
// #Q NTT
contextQ.NTT(ciphertext.value[0], ciphertext.value[0])

View File

@@ -7,10 +7,147 @@ import (
"math"
)
type evaluatorContext struct {
// Context parameters
n uint64
scale float64
// Number of available levels
levels uint64
// Moduli chain
moduli []uint64
scalechain []float64
// Contexts
specialprimes []uint64
alpha uint64
beta uint64
contextQ *ring.Context
contextP *ring.Context
contextKeys *ring.Context
// Pre-computed values for the rescaling
rescaleParamsKeys []uint64 // (P^-1) mod each qi
}
func newEvaluatorContext(params *Parameters) *evaluatorContext {
n := uint64(1 << uint64(params.LogN))
scale := params.Scale
levels := uint64(len(params.Modulichain))
alpha := uint64(len(params.P))
beta := uint64(math.Ceil(float64(levels) / float64(alpha)))
scalechain := make([]float64, len(params.Modulichain))
// Extracts all the different primes bit size and maps their number
primesbitlen := make(map[uint64]uint64)
for i, qi := range params.Modulichain {
primesbitlen[uint64(qi)]++
if uint64(params.Modulichain[i]) > 60 {
panic("provided moduli must be smaller than 61")
}
}
for _, pj := range params.P {
primesbitlen[uint64(pj)]++
if uint64(pj) > 60 {
panic("provided P must be smaller than 61")
}
}
// For each bitsize, finds that many primes
primes := make(map[uint64][]uint64)
for key, value := range primesbitlen {
primes[key] = GenerateCKKSPrimes(key, uint64(params.LogN), value)
}
// Assigns the primes to the ckks moduli chain
moduli := make([]uint64, len(params.Modulichain))
for i, qi := range params.Modulichain {
moduli[i] = primes[uint64(params.Modulichain[i])][0]
primes[uint64(qi)] = primes[uint64(qi)][1:]
scalechain[i] = float64(moduli[i])
}
// Assigns the primes to the special primes list for the the keyscontext
specialprimes := make([]uint64, len(params.P))
for i, pj := range params.P {
specialprimes[i] = primes[uint64(pj)][0]
primes[uint64(pj)] = primes[uint64(pj)][1:]
}
// Contexts
contextQ := ring.NewContext()
contextQ.SetParameters(1<<params.LogN, moduli)
err := contextQ.GenNTTParams()
if err != nil {
panic(err)
}
contextP := ring.NewContext()
contextP.SetParameters(1<<params.LogN, specialprimes)
err = contextP.GenNTTParams()
if err != nil {
panic(err)
}
contextKeys := ring.NewContext()
contextKeys.SetParameters(1<<params.LogN, append(moduli, specialprimes...))
err = contextKeys.GenNTTParams()
if err != nil {
panic(err)
}
var Qi uint64
bredParams := contextQ.GetBredParams()
rescaleParamsKeys := make([]uint64, levels)
PBig := ring.NewUint(1)
for _, pj := range specialprimes {
PBig.Mul(PBig, ring.NewUint(pj))
}
tmp := ring.NewUint(0)
for i := uint64(0); i < levels; i++ {
Qi = moduli[i]
tmp.Mod(PBig, ring.NewUint(Qi))
rescaleParamsKeys[i] = ring.MForm(ring.ModExp(ring.BRedAdd(tmp.Uint64(), Qi, bredParams[i]), Qi-2, Qi), Qi, bredParams[i])
}
return &evaluatorContext{
n: n,
scale: scale,
levels: levels,
moduli: moduli,
scalechain: scalechain,
specialprimes: specialprimes,
alpha: alpha,
beta: beta,
contextQ: contextQ,
contextP: contextP,
contextKeys: contextKeys,
rescaleParamsKeys: rescaleParamsKeys,
}
}
// Evaluator is a struct holding the necessary elements to operates the homomorphic operations between ciphertext and/or plaintexts.
// It also holds a small memory pool used to store intermediate computations.
type Evaluator struct {
ckkscontext *Context
context *evaluatorContext
ringpool [6]*ring.Poly
keyswitchpool [4]*ring.Poly
@@ -27,31 +164,32 @@ type Evaluator struct {
// NewEvaluator creates a new Evaluator, that can be used to do homomorphic
// operations on the ciphertexts and/or plaintexts. It stores a small pool of polynomials
// and ciphertexts that will be used for intermediate values.
func (ckkscontext *Context) NewEvaluator() (evaluator *Evaluator) {
func NewEvaluator(params *Parameters) (evaluator *Evaluator) {
evaluator = new(Evaluator)
evaluator.ckkscontext = ckkscontext
context := newEvaluatorContext(params)
evaluator.context = context
for i := 0; i < 5; i++ {
evaluator.ringpool[i] = evaluator.ckkscontext.contextQ.NewPoly()
evaluator.ringpool[i] = context.contextQ.NewPoly()
}
for i := 0; i < 4; i++ {
evaluator.keyswitchpool[i] = evaluator.ckkscontext.contextKeys.NewPoly()
evaluator.keyswitchpool[i] = context.contextKeys.NewPoly()
}
for i := 0; i < 3; i++ {
evaluator.poolQ[i] = evaluator.ckkscontext.contextQ.NewPoly()
evaluator.poolP[i] = evaluator.ckkscontext.contextP.NewPoly()
evaluator.poolQ[i] = context.contextQ.NewPoly()
evaluator.poolP[i] = context.contextP.NewPoly()
}
evaluator.rescalepool = make([]uint64, ckkscontext.n)
evaluator.rescalepool = make([]uint64, context.n)
evaluator.ctxpool = ckkscontext.NewCiphertext(1, ckkscontext.levels-1, ckkscontext.scale)
evaluator.ctxpool = NewCiphertext(1, context.levels-1, context.scale, context.contextQ)
evaluator.baseconverter = ring.NewFastBasisExtender(evaluator.ckkscontext.contextQ.Modulus, evaluator.ckkscontext.specialprimes)
evaluator.baseconverter = ring.NewFastBasisExtender(context.contextQ.Modulus, context.specialprimes)
evaluator.decomposer = ring.NewArbitraryDecomposer(ckkscontext.moduli, ckkscontext.specialprimes)
evaluator.decomposer = ring.NewArbitraryDecomposer(context.moduli, context.specialprimes)
return evaluator
}
@@ -94,19 +232,19 @@ func (evaluator *Evaluator) newCiphertextBinary(op0, op1 Operand) (ctOut *Cipher
maxScale := utils.MaxFloat64(op0.Scale(), op1.Scale())
minLevel := utils.MinUint64(op0.Level(), op1.Level())
return evaluator.ckkscontext.NewCiphertext(maxDegree, minLevel, maxScale)
return NewCiphertext(maxDegree, minLevel, maxScale, evaluator.context.contextQ)
}
// Add adds op0 to op1 and returns the result on ctOut.
func (evaluator *Evaluator) Add(op0, op1 Operand, ctOut *Ciphertext) {
el0, el1, elOut := evaluator.getElemAndCheckBinary(op0, op1, ctOut, utils.MaxUint64(op0.Degree(), op1.Degree()))
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.ckkscontext.contextQ.AddLvl)
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.context.contextQ.AddLvl)
}
// AddNoMod adds op0 to op1 and returns the result on ctOut, without modular reduction.
func (evaluator *Evaluator) AddNoMod(op0, op1 Operand, ctOut *Ciphertext) {
el0, el1, elOut := evaluator.getElemAndCheckBinary(op0, op1, ctOut, utils.MaxUint64(op0.Degree(), op1.Degree()))
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.ckkscontext.contextQ.AddNoModLvl)
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.context.contextQ.AddNoModLvl)
}
// AddNew adds op0 to op1 and returns the result on a newly created element.
@@ -128,13 +266,13 @@ func (evaluator *Evaluator) Sub(op0, op1 Operand, ctOut *Ciphertext) {
el0, el1, elOut := evaluator.getElemAndCheckBinary(op0, op1, ctOut, utils.MaxUint64(op0.Degree(), op1.Degree()))
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.ckkscontext.contextQ.SubLvl)
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.context.contextQ.SubLvl)
level := utils.MinUint64(utils.MinUint64(el0.Level(), el1.Level()), elOut.Level())
if el0.Degree() < el1.Degree() {
for i := el0.Degree() + 1; i < el1.Degree()+1; i++ {
evaluator.ckkscontext.contextQ.NegLvl(level, elOut.Value()[i], elOut.Value()[i])
evaluator.context.contextQ.NegLvl(level, elOut.Value()[i], elOut.Value()[i])
}
}
@@ -145,13 +283,13 @@ func (evaluator *Evaluator) SubNoMod(op0, op1 Operand, ctOut *Ciphertext) {
el0, el1, elOut := evaluator.getElemAndCheckBinary(op0, op1, ctOut, utils.MaxUint64(op0.Degree(), op1.Degree()))
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.ckkscontext.contextQ.SubNoModLvl)
evaluator.evaluateInPlace(el0, el1, elOut, evaluator.context.contextQ.SubNoModLvl)
level := utils.MinUint64(utils.MinUint64(el0.Level(), el1.Level()), elOut.Level())
if el0.Degree() < el1.Degree() {
for i := el0.Degree() + 1; i < el1.Degree()+1; i++ {
evaluator.ckkscontext.contextQ.NegLvl(level, elOut.Value()[i], elOut.Value()[i])
evaluator.context.contextQ.NegLvl(level, elOut.Value()[i], elOut.Value()[i])
}
}
@@ -181,7 +319,7 @@ func (evaluator *Evaluator) evaluateInPlace(c0, c1, ctOut *ckksElement, evaluate
minDegree := utils.MinUint64(c0.Degree(), c1.Degree())
// Else resizes the receiver element
ctOut.Resize(evaluator.ckkscontext, maxDegree)
ctOut.Resize(evaluator.context.contextQ, maxDegree)
evaluator.DropLevel(ctOut.Ciphertext(), ctOut.Level()-utils.MinUint64(c0.Level(), c1.Level()))
// Checks wether or not the receiver element is the same as one of the input elements
@@ -279,11 +417,11 @@ func (evaluator *Evaluator) evaluateInPlace(c0, c1, ctOut *ckksElement, evaluate
if c0.Degree() > c1.Degree() && tmp0 != ctOut {
for i := minDegree + 1; i < maxDegree+1; i++ {
evaluator.ckkscontext.contextQ.CopyLvl(level, tmp0.Value()[i], ctOut.Value()[i])
evaluator.context.contextQ.CopyLvl(level, tmp0.Value()[i], ctOut.Value()[i])
}
} else if c1.Degree() > c0.Degree() && tmp1 != ctOut {
for i := minDegree + 1; i < maxDegree+1; i++ {
evaluator.ckkscontext.contextQ.CopyLvl(level, tmp1.Value()[i], ctOut.Value()[i])
evaluator.context.contextQ.CopyLvl(level, tmp1.Value()[i], ctOut.Value()[i])
}
}
}
@@ -298,13 +436,13 @@ func (evaluator *Evaluator) Neg(ct0 *Ciphertext, ctOut *Ciphertext) {
}
for i := range ct0.value {
evaluator.ckkscontext.contextQ.NegLvl(level, ct0.value[i], ctOut.Value()[i])
evaluator.context.contextQ.NegLvl(level, ct0.value[i], ctOut.Value()[i])
}
}
// NegNew negates ct0 and returns the result on a newly created element.
func (evaluator *Evaluator) NegNew(ct0 *Ciphertext) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.Neg(ct0, ctOut)
return
}
@@ -349,7 +487,7 @@ func (evaluator *Evaluator) AddConst(ct0 *Ciphertext, constant interface{}, ctOu
var scaledConst, scaledConstReal, scaledConstImag uint64
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
// Component wise addition of the following vector to the ciphertext :
// [a + b*psi_qi^2, ....., a + b*psi_qi^2, a - b*psi_qi^2, ...., a - b*psi_qi^2] mod Qi
@@ -376,7 +514,7 @@ func (evaluator *Evaluator) AddConst(ct0 *Ciphertext, constant interface{}, ctOu
p1tmp := ctOut.Value()[0].Coeffs[i]
p0tmp := ct0.value[0].Coeffs[i]
for j := uint64(0); j < evaluator.ckkscontext.n>>1; j++ {
for j := uint64(0); j < evaluator.context.n>>1; j++ {
p1tmp[j] = ring.CRed(p0tmp[j]+scaledConst, qi)
}
@@ -384,7 +522,7 @@ func (evaluator *Evaluator) AddConst(ct0 *Ciphertext, constant interface{}, ctOu
scaledConst = ring.CRed(scaledConstReal+(qi-scaledConstImag), qi)
}
for j := evaluator.ckkscontext.n >> 1; j < evaluator.ckkscontext.n; j++ {
for j := evaluator.context.n >> 1; j < evaluator.context.n; j++ {
p1tmp[j] = ring.CRed(p0tmp[j]+scaledConst, qi)
}
}
@@ -421,7 +559,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
valueFloat := cReal - float64(valueInt)
if valueFloat != 0 {
scale = evaluator.ckkscontext.scale
scale = evaluator.context.scale
}
}
@@ -430,7 +568,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
valueFloat := cImag - float64(valueInt)
if valueFloat != 0 {
scale = evaluator.ckkscontext.scale
scale = evaluator.context.scale
}
}
@@ -443,7 +581,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
valueFloat := cReal - float64(valueInt)
if valueFloat != 0 {
scale = evaluator.ckkscontext.scale
scale = evaluator.context.scale
}
}
@@ -462,7 +600,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
var scaledConst, scaledConstReal, scaledConstImag uint64
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
// If a scaling will be required to multiply by the constant,
// equalizes scales such that the scales match in the end.
@@ -470,19 +608,19 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
// If ctOut scaling is smaller than ct0's scale + the default scaling,
// then brings ctOut scale to ct0's scale.
if ctOut.Scale() < ct0.Scale()*evaluator.ckkscontext.scale {
if ctOut.Scale() < ct0.Scale()*evaluator.context.scale {
if uint64((evaluator.ckkscontext.scale*ct0.Scale())/ctOut.Scale()) != 0 {
if uint64((evaluator.context.scale*ct0.Scale())/ctOut.Scale()) != 0 {
evaluator.MultByConst(ctOut, uint64((evaluator.ckkscontext.scale*ct0.Scale())/ctOut.Scale()), ctOut)
evaluator.MultByConst(ctOut, uint64((evaluator.context.scale*ct0.Scale())/ctOut.Scale()), ctOut)
}
ctOut.SetScale(evaluator.ckkscontext.scale * ct0.Scale())
ctOut.SetScale(evaluator.context.scale * ct0.Scale())
// If ctOut.Scale() > ((a+bi)*scale)*ct0(x), then sets the scale to
// bring c(x)*scale to the level of ctOut(x) scale
} else if ctOut.Scale() > ct0.Scale()*evaluator.ckkscontext.scale {
} else if ctOut.Scale() > ct0.Scale()*evaluator.context.scale {
scale = ctOut.Scale() / ct0.Scale()
}
@@ -534,7 +672,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
for u := range ct0.Value() {
p0tmp := ct0.Value()[u].Coeffs[i]
p1tmp := ctOut.Value()[u].Coeffs[i]
for j := uint64(0); j < evaluator.ckkscontext.n>>1; j++ {
for j := uint64(0); j < evaluator.context.n>>1; j++ {
p1tmp[j] = ring.CRed(p1tmp[j]+ring.MRed(p0tmp[j], scaledConst, qi, mredParams), qi)
}
}
@@ -547,7 +685,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
for u := range ct0.Value() {
p0tmp := ct0.Value()[u].Coeffs[i]
p1tmp := ctOut.Value()[u].Coeffs[i]
for j := evaluator.ckkscontext.n >> 1; j < evaluator.ckkscontext.n; j++ {
for j := evaluator.context.n >> 1; j < evaluator.context.n; j++ {
p1tmp[j] = ring.CRed(p1tmp[j]+ring.MRed(p0tmp[j], scaledConst, qi, mredParams), qi)
}
}
@@ -558,7 +696,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac
// The scale of the output element will depend on the scale of the input element and the constant (if the constant
// needs to be scaled (its rational part is not zero)). The constant can be an uint64, int64, float64 or complex128.
func (evaluator *Evaluator) MultByConstNew(ct0 *Ciphertext, constant interface{}) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.MultByConst(ct0, constant, ctOut)
return
}
@@ -587,7 +725,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c
valueFloat := cReal - float64(valueInt)
if valueFloat != 0 {
scale = evaluator.ckkscontext.scale
scale = evaluator.context.scale
}
}
@@ -596,7 +734,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c
valueFloat := cImag - float64(valueInt)
if valueFloat != 0 {
scale = evaluator.ckkscontext.scale
scale = evaluator.context.scale
}
}
@@ -609,7 +747,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c
valueFloat := cReal - float64(valueInt)
if valueFloat != 0 {
scale = evaluator.ckkscontext.scale
scale = evaluator.context.scale
}
}
@@ -630,7 +768,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c
// [a + b*psi_qi^2, ....., a + b*psi_qi^2, a - b*psi_qi^2, ...., a - b*psi_qi^2] mod Qi
// [{ N/2 }{ N/2 }]
// Which is equivalent outside of the NTT domain of adding a to the first coefficient of ct0 and b to the N/2th coefficient of ct0.
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
var scaledConst, scaledConstReal, scaledConstImag uint64
for i := uint64(0); i < level+1; i++ {
@@ -658,7 +796,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c
for u := range ct0.Value() {
p0tmp := ct0.Value()[u].Coeffs[i]
p1tmp := ctOut.Value()[u].Coeffs[i]
for j := uint64(0); j < evaluator.ckkscontext.n>>1; j++ {
for j := uint64(0); j < evaluator.context.n>>1; j++ {
p1tmp[j] = ring.MRed(p0tmp[j], scaledConst, qi, mredParams)
}
}
@@ -671,7 +809,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c
for u := range ct0.Value() {
p0tmp := ct0.Value()[u].Coeffs[i]
p1tmp := ctOut.Value()[u].Coeffs[i]
for j := evaluator.ckkscontext.n >> 1; j < evaluator.ckkscontext.n; j++ {
for j := evaluator.context.n >> 1; j < evaluator.context.n; j++ {
p1tmp[j] = ring.MRed(p0tmp[j], scaledConst, qi, mredParams)
}
}
@@ -683,7 +821,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c
// MultByiNew multiplies ct0 by the imaginary number i, and returns the result on a newly created element.
// Does not change the scale.
func (evaluator *Evaluator) MultByiNew(ct0 *Ciphertext) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(1, ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.MultByi(ct0, ctOut)
return ctOut
}
@@ -696,7 +834,7 @@ func (evaluator *Evaluator) MultByi(ct0 *Ciphertext, ct1 *Ciphertext) {
level = utils.MinUint64(ct0.Level(), ct1.Level())
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
var imag uint64
@@ -732,7 +870,7 @@ func (evaluator *Evaluator) MultByi(ct0 *Ciphertext, ct1 *Ciphertext) {
// DivByiNew multiplies ct0 by the imaginary number 1/i = -i, and returns the result on a newly created element.
// Does not change the scale.
func (evaluator *Evaluator) DivByiNew(ct0 *Ciphertext) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(1, ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.DivByi(ct0, ctOut)
return
}
@@ -745,7 +883,7 @@ func (evaluator *Evaluator) DivByi(ct0 *Ciphertext, ct1 *Ciphertext) {
level = utils.MinUint64(ct0.Level(), ct1.Level())
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
var imag uint64
@@ -780,7 +918,7 @@ func (evaluator *Evaluator) DivByi(ct0 *Ciphertext, ct1 *Ciphertext) {
// ScaleUpNew multiplies ct0 by 2^scale and sets its scale to its previous scale
// plus 2^n. Returns the result on a newly created element.
func (evaluator *Evaluator) ScaleUpNew(ct0 *Ciphertext, scale float64) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.ScaleUp(ct0, scale, ctOut)
return
}
@@ -795,7 +933,7 @@ func (evaluator *Evaluator) ScaleUp(ct0 *Ciphertext, scale float64, ctOut *Ciphe
// MulByPow2New multiplies the ct0 by 2^pow2 and returns the result on a newly created element.
func (evaluator *Evaluator) MulByPow2New(ct0 *Ciphertext, pow2 uint64) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.MulByPow2(ct0.Element(), pow2, ctOut.Element())
return
}
@@ -805,7 +943,7 @@ func (evaluator *Evaluator) MulByPow2(ct0 *ckksElement, pow2 uint64, ctOut *ckks
var level uint64
level = utils.MinUint64(ct0.Level(), ctOut.Level())
for i := range ctOut.Value() {
evaluator.ckkscontext.contextQ.MulByPow2Lvl(level, ct0.value[i], pow2, ctOut.Value()[i])
evaluator.context.contextQ.MulByPow2Lvl(level, ct0.value[i], pow2, ctOut.Value()[i])
}
}
@@ -813,7 +951,7 @@ func (evaluator *Evaluator) MulByPow2(ct0 *ckksElement, pow2 uint64, ctOut *ckks
// To be used in conjonction with function not applying modular reduction.
func (evaluator *Evaluator) ReduceNew(ct0 *Ciphertext) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
_ = evaluator.Reduce(ct0, ctOut)
@@ -829,7 +967,7 @@ func (evaluator *Evaluator) Reduce(ct0 *Ciphertext, ctOut *Ciphertext) error {
}
for i := range ct0.value {
evaluator.ckkscontext.contextQ.ReduceLvl(utils.MinUint64(ct0.Level(), ctOut.Level()), ct0.value[i], ctOut.value[i])
evaluator.context.contextQ.ReduceLvl(utils.MinUint64(ct0.Level(), ctOut.Level()), ct0.value[i], ctOut.value[i])
}
return nil
@@ -867,7 +1005,7 @@ func (evaluator *Evaluator) DropLevel(ct0 *Ciphertext, levels uint64) (err error
// some error.
func (evaluator *Evaluator) RescaleNew(ct0 *Ciphertext, threshold float64) (ctOut *Ciphertext, err error) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
return ctOut, evaluator.Rescale(ct0, threshold, ctOut)
}
@@ -887,7 +1025,7 @@ func (evaluator *Evaluator) Rescale(ct0 *Ciphertext, threshold float64, c1 *Ciph
panic("cannot rescale -> reciever ciphertext does not match input ciphertext level")
}
if ct0.Scale() >= (threshold*evaluator.ckkscontext.scalechain[c1.Level()])/2 {
if ct0.Scale() >= (threshold*evaluator.context.scalechain[c1.Level()])/2 {
if !ct0.IsNTT() {
panic("cannot rescale -> input ciphertext not in NTT")
@@ -895,12 +1033,12 @@ func (evaluator *Evaluator) Rescale(ct0 *Ciphertext, threshold float64, c1 *Ciph
c1.Copy(ct0.Element())
for c1.Scale() >= (threshold*evaluator.ckkscontext.scalechain[c1.Level()])/2 && c1.Level() != 0 {
for c1.Scale() >= (threshold*evaluator.context.scalechain[c1.Level()])/2 && c1.Level() != 0 {
c1.DivScale(evaluator.ckkscontext.scalechain[c1.Level()])
c1.DivScale(evaluator.context.scalechain[c1.Level()])
for i := range c1.Value() {
evaluator.ckkscontext.contextQ.DivRoundByLastModulusNTT(c1.Value()[i])
evaluator.context.contextQ.DivRoundByLastModulusNTT(c1.Value()[i])
}
}
@@ -930,11 +1068,11 @@ func (evaluator *Evaluator) RescaleMany(ct0 *Ciphertext, nbRescales uint64, c1 *
c1.Copy(ct0.Element())
for i := uint64(0); i < nbRescales; i++ {
c1.DivScale(evaluator.ckkscontext.scalechain[c1.Level()-i])
c1.DivScale(evaluator.context.scalechain[c1.Level()-i])
}
for i := range c1.Value() {
evaluator.ckkscontext.contextQ.DivRoundByLastModulusManyNTT(c1.Value()[i], nbRescales)
evaluator.context.contextQ.DivRoundByLastModulusManyNTT(c1.Value()[i], nbRescales)
}
return nil
@@ -947,7 +1085,7 @@ func (evaluator *Evaluator) RescaleMany(ct0 *Ciphertext, nbRescales uint64, c1 *
// the resulting ciphertext will be of degree two. This function only accepts plaintexts (degree zero) and/or ciphertexts of degree one.
func (evaluator *Evaluator) MulRelinNew(op0, op1 Operand, evakey *EvaluationKey) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(1, utils.MinUint64(op0.Level(), op1.Level()), op0.Scale()+op1.Scale())
ctOut = NewCiphertext(1, utils.MinUint64(op0.Level(), op1.Level()), op0.Scale()+op1.Scale(), evaluator.context.contextQ)
evaluator.MulRelin(op0, op1, evakey, ctOut)
return ctOut
@@ -982,7 +1120,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct
elOut.SetScale(el0.Scale() * el1.Scale())
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
var c00, c01, c0, c1, c2 *ring.Poly
@@ -1003,7 +1141,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct
// resizes the cipher text to a degree 2 ciphertext
if evakey == nil {
elOut.Resize(evaluator.ckkscontext, 2)
elOut.Resize(evaluator.context.contextQ, 2)
c2 = elOut.value[2]
// If there is however an evaluation key, then
@@ -1050,7 +1188,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct
} else { // Or copies the result on the output ciphertext if it was one of the inputs
if elOut == el0 || elOut == el1 {
elOut.Resize(evaluator.ckkscontext, 2)
elOut.Resize(evaluator.context.contextQ, 2)
context.CopyLvl(level, c0, elOut.value[0])
context.CopyLvl(level, c1, elOut.value[1])
context.CopyLvl(level, c2, elOut.value[2])
@@ -1080,7 +1218,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct
// RelinearizeNew applies the relinearization procedure on ct0 and returns the result on a newly
// created ciphertext. Requires the input ciphertext to be of degree two.
func (evaluator *Evaluator) RelinearizeNew(ct0 *Ciphertext, evakey *EvaluationKey) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(1, ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.Relinearize(ct0, evakey, ctOut)
return
}
@@ -1096,21 +1234,21 @@ func (evaluator *Evaluator) Relinearize(ct0 *Ciphertext, evakey *EvaluationKey,
}
level := utils.MinUint64(ct0.Level(), ctOut.Level())
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
context.CopyLvl(level, ct0.value[0], ctOut.value[0])
context.CopyLvl(level, ct0.value[1], ctOut.value[1])
evaluator.switchKeysInPlace(ct0.value[2], evakey.evakey, ctOut)
ctOut.Resize(evaluator.ckkscontext, 1)
ctOut.Resize(evaluator.context.contextQ, 1)
}
// SwitchKeysNew re-encrypts ct0 under a different key and returns the result on a newly created element.
// Requires a switchinkey, which is computed from the key under which the ciphertext is currently encrypted,
// and the key under which the ciphertext will be re-encrypted.
func (evaluator *Evaluator) SwitchKeysNew(ct0 *Ciphertext, switchingKey *SwitchingKey) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.SwitchKeys(ct0, switchingKey, ctOut)
return
}
@@ -1125,7 +1263,7 @@ func (evaluator *Evaluator) SwitchKeys(ct0 *Ciphertext, switchingKey *SwitchingK
}
level := utils.MinUint64(ct0.Level(), ctOut.Level())
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
context.CopyLvl(level, ct0.value[0], ctOut.value[0])
context.CopyLvl(level, ct0.value[1], ctOut.value[1])
@@ -1136,7 +1274,7 @@ func (evaluator *Evaluator) SwitchKeys(ct0 *Ciphertext, switchingKey *SwitchingK
// RotateColumnsNew rotates the columns of ct0 by k position to the left, and returns the result on a newly created element.
// If the provided element is a ciphertext, a keyswitching operation is necessary and a rotation key for the specific rotation needs to be provided.
func (evaluator *Evaluator) RotateColumnsNew(ct0 *Ciphertext, k uint64, evakey *RotationKeys) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.RotateColumns(ct0, k, evakey, ctOut)
return
}
@@ -1149,7 +1287,7 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot
panic("cannot rotate -> input and output ciphertext must be of degree 1")
}
k &= ((evaluator.ckkscontext.n >> 1) - 1)
k &= ((evaluator.context.n >> 1) - 1)
if k == 0 {
@@ -1168,7 +1306,7 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot
// If not looks if the left and right pow2 rotations have been generated
hasPow2Rotations := true
for i := uint64(1); i < evaluator.ckkscontext.n>>1; i <<= 1 {
for i := uint64(1); i < evaluator.context.n>>1; i <<= 1 {
if evakey.evakeyRotColLeft[i] == nil || evakey.evakeyRotColRight[i] == nil {
hasPow2Rotations = false
break
@@ -1178,10 +1316,10 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot
// If yes, computes the least amount of rotation between left and right required to apply the demanded rotation
if hasPow2Rotations {
if utils.HammingWeight64(k) <= utils.HammingWeight64((evaluator.ckkscontext.n>>1)-k) {
if utils.HammingWeight64(k) <= utils.HammingWeight64((evaluator.context.n>>1)-k) {
evaluator.rotateColumnsLPow2(ct0, k, evakey, ctOut)
} else {
evaluator.rotateColumnsRPow2(ct0, (evaluator.ckkscontext.n>>1)-k, evakey, ctOut)
evaluator.rotateColumnsRPow2(ct0, (evaluator.context.n>>1)-k, evakey, ctOut)
}
// Else returns an error indicating that the keys have not been generated
@@ -1197,17 +1335,17 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot
func (evaluator *Evaluator) RotateHoisted(ctIn *Ciphertext, rotations []uint64, rotkeys *RotationKeys) (cOut map[uint64]*Ciphertext) {
// Pre-computation for rotations using hoisting
contextQ := evaluator.ckkscontext.contextQ
contextP := evaluator.ckkscontext.contextP
contextQ := evaluator.context.contextQ
contextP := evaluator.context.contextP
c2NTT := ctIn.value[1]
c2InvNTT := contextQ.NewPoly()
contextQ.InvNTTLvl(ctIn.Level(), c2NTT, c2InvNTT)
c2QiQDecomp := make([]*ring.Poly, evaluator.ckkscontext.beta)
c2QiPDecomp := make([]*ring.Poly, evaluator.ckkscontext.beta)
c2QiQDecomp := make([]*ring.Poly, evaluator.context.beta)
c2QiPDecomp := make([]*ring.Poly, evaluator.context.beta)
alpha := evaluator.ckkscontext.alpha
alpha := evaluator.context.alpha
beta := uint64(math.Ceil(float64(ctIn.Level()+1) / float64(alpha)))
for i := uint64(0); i < beta; i++ {
@@ -1220,12 +1358,12 @@ func (evaluator *Evaluator) RotateHoisted(ctIn *Ciphertext, rotations []uint64,
for _, i := range rotations {
i &= ((evaluator.ckkscontext.n >> 1) - 1)
i &= ((evaluator.context.n >> 1) - 1)
if i == 0 {
cOut[i] = ctIn.CopyNew().Ciphertext()
} else {
cOut[i] = evaluator.ckkscontext.NewCiphertext(1, ctIn.Level(), ctIn.Scale())
cOut[i] = NewCiphertext(1, ctIn.Level(), ctIn.Scale(), evaluator.context.contextQ)
evaluator.switchKeyHoisted(ctIn, c2QiQDecomp, c2QiPDecomp, i, rotkeys, cOut[i])
}
}
@@ -1251,8 +1389,8 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi
level = ctOut.Level()
contextQ := evaluator.ckkscontext.contextQ
contextP := evaluator.ckkscontext.contextP
contextQ := evaluator.context.contextQ
contextP := evaluator.context.contextP
if ctIn != ctOut {
ring.PermuteNTTWithIndex(ctIn.value[0], evakey.permuteNTTLeftIndex[k], evaluator.ringpool[0])
@@ -1283,7 +1421,7 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi
reduce = 0
alpha := evaluator.ckkscontext.alpha
alpha := evaluator.context.alpha
beta := uint64(math.Ceil(float64(level+1) / float64(alpha)))
// Key switching with crt decomposition for the Qi
@@ -1296,7 +1434,7 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi
contextQ.MulCoeffsMontgomeryAndAddNoModLvl(level, evakey.evakeyRotColLeft[k].evakey[i][1], c2QiQPermute, pool3Q)
// We continue with the keyswitch primes.
for j, keysindex := uint64(0), evaluator.ckkscontext.levels; j < uint64(len(evaluator.ckkscontext.specialprimes)); j, keysindex = j+1, keysindex+1 {
for j, keysindex := uint64(0), evaluator.context.levels; j < uint64(len(evaluator.context.specialprimes)); j, keysindex = j+1, keysindex+1 {
pj := contextP.Modulus[j]
mredParams := contextP.GetMredParams()[j]
@@ -1332,8 +1470,8 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi
//Independant of context (parameter : level)
// Computes pool2Q = pool2Q/pool2P and pool3Q = pool3Q/pool3P
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0])
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0])
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0])
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0])
//Independant of context (parameter : level)
contextQ.AddLvl(level, ctOut.value[0], pool2Q, ctOut.value[0])
@@ -1355,7 +1493,7 @@ func (evaluator *Evaluator) rotateColumnsPow2(ct0 *Ciphertext, k uint64, permute
evakeyIndex = 1
level := utils.MinUint64(ct0.Level(), ctOut.Level())
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
context.CopyLvl(level, ct0.value[0], ctOut.value[0])
context.CopyLvl(level, ct0.value[1], ctOut.value[1])
@@ -1376,7 +1514,7 @@ func (evaluator *Evaluator) rotateColumnsPow2(ct0 *Ciphertext, k uint64, permute
// created element. If the provided element is a ciphertext, a keyswitching operation is necessary and a rotation key
// for the row rotation needs to be provided.
func (evaluator *Evaluator) ConjugateNew(ct0 *Ciphertext, evakey *RotationKeys) (ctOut *Ciphertext) {
ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale())
ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ)
evaluator.Conjugate(ct0, evakey, ctOut)
return
}
@@ -1412,7 +1550,7 @@ func (evaluator *Evaluator) permuteNTT(ct0 *Ciphertext, index []uint64, evakey *
ring.PermuteNTTWithIndex(ct0.value[1], index, el1)
level := utils.MinUint64(ct0.Level(), ctOut.Level())
context := evaluator.ckkscontext.contextQ
context := evaluator.context.contextQ
context.CopyLvl(level, el0, ctOut.value[0])
context.CopyLvl(level, el1, ctOut.value[1])
@@ -1426,8 +1564,8 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe
level = ctOut.Level()
contextQ := evaluator.ckkscontext.contextQ
contextP := evaluator.ckkscontext.contextP
contextQ := evaluator.context.contextQ
contextP := evaluator.context.contextP
for i := range evaluator.poolQ {
evaluator.poolQ[i].Zero()
@@ -1456,7 +1594,7 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe
reduce = 0
alpha := evaluator.ckkscontext.alpha
alpha := evaluator.context.alpha
beta := uint64(math.Ceil(float64(level+1) / float64(alpha)))
// Key switching with crt decomposition for the Qi
@@ -1468,7 +1606,7 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe
contextQ.MulCoeffsMontgomeryAndAddNoModLvl(level, evakey.evakey[i][1], c2QiQ, pool3Q)
// We continue with the keyswitch primes.
for j, keysindex := uint64(0), evaluator.ckkscontext.levels; j < uint64(len(evaluator.ckkscontext.specialprimes)); j, keysindex = j+1, keysindex+1 {
for j, keysindex := uint64(0), evaluator.context.levels; j < uint64(len(evaluator.context.specialprimes)); j, keysindex = j+1, keysindex+1 {
pj := contextP.Modulus[j]
mredParams := contextP.GetMredParams()[j]
@@ -1505,8 +1643,8 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe
//Independant of context (parameter : level)
// Computes pool2Q = pool2Q/pool2P and pool3Q = pool3Q/pool3P
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0])
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0])
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0])
evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0])
//Independant of context (parameter : level)
contextQ.AddLvl(level, ctOut.value[0], pool2Q, ctOut.value[0])
@@ -1517,12 +1655,12 @@ func (evaluator *Evaluator) decomposeAndSplitNTT(level, beta uint64, c2NTT, c2In
// Decomposes the input polynomial into the target CRT basis.
contextQ := evaluator.ckkscontext.contextQ
contextP := evaluator.ckkscontext.contextP
contextQ := evaluator.context.contextQ
contextP := evaluator.context.contextP
evaluator.decomposer.DecomposeAndSplit(level, beta, c2InvNTT, c2QiQ, c2QiP)
p0idxst := beta * evaluator.ckkscontext.alpha
p0idxst := beta * evaluator.context.alpha
p0idxed := p0idxst + evaluator.decomposer.Xalpha()[beta]
// c2_qi = cx mod qi mod qi

View File

@@ -75,12 +75,12 @@ func (el *ckksElement) DivScale(scale float64) {
}
// Resize resizes the degree of the target element.
func (el *ckksElement) Resize(ckkscontext *Context, degree uint64) {
func (el *ckksElement) Resize(ringCtx *ring.Context, degree uint64) {
if el.Degree() > degree {
el.value = el.value[:degree+1]
} else if el.Degree() < degree {
for el.Degree() < degree {
el.value = append(el.value, []*ring.Poly{ckkscontext.contextQ.NewPolyLvl(el.Level())}...)
el.value = append(el.value, []*ring.Poly{ringCtx.NewPolyLvl(el.Level())}...)
}
}
}

View File

@@ -88,7 +88,7 @@ func computePowerBasis(n uint64, C map[uint64]*Ciphertext, evaluator *Evaluator,
// Computes C[n] = C[a]*C[b]
C[n] = evaluator.MulRelinNew(C[a], C[b], evakey)
evaluator.Rescale(C[n], evaluator.ckkscontext.scale, C[n])
evaluator.Rescale(C[n], evaluator.context.scale, C[n])
}
}
@@ -133,7 +133,7 @@ func recurse(maxDegree, L, M uint64, coeffs map[uint64]complex128, C map[uint64]
evaluator.Add(res, tmp, res)
evaluator.Rescale(res, evaluator.ckkscontext.scale, res)
evaluator.Rescale(res, evaluator.context.scale, res)
return res
@@ -141,7 +141,7 @@ func recurse(maxDegree, L, M uint64, coeffs map[uint64]complex128, C map[uint64]
func evaluatePolyFromPowerBasis(coeffs map[uint64]complex128, C map[uint64]*Ciphertext, evaluator *Evaluator, evakey *EvaluationKey) (res *Ciphertext) {
res = evaluator.ckkscontext.NewCiphertext(1, C[1].Level(), C[1].Scale())
res = NewCiphertext(1, C[1].Level(), C[1].Scale(), evaluator.context.contextQ)
if math.Abs(real(coeffs[0])) > 1e-15 || math.Abs(imag(coeffs[0])) > 1e-15 {
evaluator.AddConst(res, coeffs[0], res)
@@ -153,7 +153,7 @@ func evaluatePolyFromPowerBasis(coeffs map[uint64]complex128, C map[uint64]*Ciph
}
}
evaluator.Rescale(res, evaluator.ckkscontext.scale, res)
evaluator.Rescale(res, evaluator.context.scale, res)
return
}

View File

@@ -371,6 +371,8 @@ func benchRefresh(b *testing.B) {
contextKeys := bfvContext.ContextKeys()
sk0Shards := params.sk0Shards
ringCtx := bfv.NewCiphertextRingContext(&parameters)
type Party struct {
*RefreshProtocol
s *ring.Poly
@@ -403,13 +405,6 @@ func benchRefresh(b *testing.B) {
})
b.Run(testString("Finalize", &parameters), func(b *testing.B) {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ctOut := bfv.NewCiphertext(1, ringCtx)
for i := 0; i < b.N; i++ {
p.Finalize(ciphertext, crp, p.share, ctOut) // TODO: why does this fail ?

View File

@@ -165,12 +165,7 @@ func testRelinKeyGen(t *testing.T) {
parties := testParams.parties
for _, parameters := range testParams.contexts {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := bfv.NewCiphertextRingContext(&parameters)
params := genDBFVContext(&parameters)
@@ -263,12 +258,7 @@ func testRelinKeyGenNaive(t *testing.T) {
parties := testParams.parties
for _, parameters := range testParams.contexts {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := bfv.NewCiphertextRingContext(&parameters)
params := genDBFVContext(&parameters)
@@ -344,12 +334,7 @@ func testKeyswitching(t *testing.T) {
parties := testParams.parties
for _, parameters := range testParams.contexts {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := bfv.NewCiphertextRingContext(&parameters)
params := genDBFVContext(&parameters)
@@ -408,12 +393,7 @@ func testPublicKeySwitching(t *testing.T) {
parties := testParams.parties
for _, parameters := range testParams.contexts {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := bfv.NewCiphertextRingContext(&parameters)
params := genDBFVContext(&parameters)
@@ -529,12 +509,7 @@ func testRotKeyGenRotCols(t *testing.T) {
parties := testParams.parties
for _, parameters := range testParams.contexts {
ringCtx := ring.NewContext()
ringCtx.SetParameters(parameters.N, parameters.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := bfv.NewCiphertextRingContext(&parameters)
params := genDBFVContext(&parameters)

View File

@@ -373,7 +373,9 @@ func benchRefresh(b *testing.B) {
crpGenerator.Seed([]byte{})
crp := crpGenerator.ClockNew()
ciphertext := ckksContext.NewCiphertext(1, levelStart, ckksContext.Scale())
ringCtx := ckks.NewCiphertextRingContext(parameters)
ciphertext := ckks.NewCiphertext(1, levelStart, ckksContext.Scale(), ringCtx)
ckksContext.ContextQ().UniformPoly(ciphertext.Value()[0])
ckksContext.ContextQ().UniformPoly(ciphertext.Value()[1])

View File

@@ -78,7 +78,7 @@ func genDCKKSContext(contextParameters *ckks.Parameters) (params *dckksContext)
params.ckksContext = ckks.NewContext(contextParameters)
params.encoder = ckks.NewEncoder(contextParameters)
params.evaluator = params.ckksContext.NewEvaluator()
params.evaluator = ckks.NewEvaluator(contextParameters)
kgen := params.ckksContext.NewKeyGenerator()
@@ -105,7 +105,7 @@ func genDCKKSContext(contextParameters *ckks.Parameters) (params *dckksContext)
params.pk0 = kgen.NewPublicKey(params.sk0)
params.pk1 = kgen.NewPublicKey(params.sk1)
params.encryptorPk0 = params.ckksContext.NewEncryptorFromPk(params.pk0)
params.encryptorPk0 = ckks.NewEncryptorFromPk(params.pk0, contextParameters)
params.decryptorSk0 = params.ckksContext.NewDecryptor(params.sk0)
@@ -166,7 +166,7 @@ func testPublicKeyGen(t *testing.T) {
P0.GenPublicKey(P0.s1, crp, pk)
// Verifies that decrypt((encryptp(collectiveSk, m), collectivePk) = m
encryptorTest := ckksContext.NewEncryptorFromPk(pk)
encryptorTest := ckks.NewEncryptorFromPk(pk, parameters)
coeffs, _, ciphertext := newTestVectors(params, encryptorTest, 1, t)
@@ -369,6 +369,8 @@ func testKeyswitching(t *testing.T) {
sk0Shards := params.sk0Shards
sk1Shards := params.sk1Shards
ringCtx := ckks.NewCiphertextRingContext(parameters)
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/scale=%f",
parties,
ckksContext.LogN(),
@@ -405,7 +407,7 @@ func testKeyswitching(t *testing.T) {
}
}
ksCiphertext := ckksContext.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale())
ksCiphertext := ckks.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale(), ringCtx)
P0.KeySwitch(P0.share, ciphertext, ksCiphertext)
@@ -433,6 +435,8 @@ func testPublicKeySwitching(t *testing.T) {
sk0Shards := params.sk0Shards
pk1 := params.pk1
ringCtx := ckks.NewCiphertextRingContext(parameters)
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/scale=%f",
parties,
ckksContext.LogN(),
@@ -461,7 +465,7 @@ func testPublicKeySwitching(t *testing.T) {
}
P0 := pcksParties[0]
ciphertextSwitched := ckksContext.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale())
ciphertextSwitched := ckks.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale(), ringCtx)
for i, p := range pcksParties {
p.GenShare(p.s, pk1, ciphertext, p.share)
@@ -559,6 +563,8 @@ func testRotKeyGenCols(t *testing.T) {
decryptorSk0 := params.decryptorSk0
sk0Shards := params.sk0Shards
ringCtx := ckks.NewCiphertextRingContext(parameters)
t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/scale=%f", parties, ckksContext.LogN(), ckksContext.LogQ(), ckksContext.Levels(), ckksContext.Scale()), func(t *testing.T) {
type Party struct {
@@ -590,7 +596,7 @@ func testRotKeyGenCols(t *testing.T) {
coeffs, _, ciphertext := newTestVectors(params, encryptorPk0, 1, t)
receiver := ckksContext.NewCiphertext(ciphertext.Degree(), ciphertext.Level(), ciphertext.Scale())
receiver := ckks.NewCiphertext(ciphertext.Degree(), ciphertext.Level(), ciphertext.Scale(), ringCtx)
for k := uint64(1); k < contextKeys.N>>1; k <<= 1 {

View File

@@ -47,7 +47,7 @@ func chebyshevinterpolation() {
// Encryptor
var encryptor *ckks.Encryptor
encryptor = ckkscontext.NewEncryptorFromPk(pk)
encryptor = ckks.NewEncryptorFromPk(pk, params)
// Decryptor
var decryptor *ckks.Decryptor
@@ -55,7 +55,7 @@ func chebyshevinterpolation() {
// Evaluator
var evaluator *ckks.Evaluator
evaluator = ckkscontext.NewEvaluator()
evaluator = ckks.NewEvaluator(params)
// Values to encrypt
values := make([]complex128, ckkscontext.Slots())

View File

@@ -231,12 +231,7 @@ func main() {
plainMask := make([]*bfv.Plaintext, N, N)
encPartial := make([]*bfv.Ciphertext, N, N)
ringCtx := ring.NewContext()
ringCtx.SetParameters(params.N, params.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := bfv.NewCiphertextRingContext(params)
// Ciphertexts to be retrieved.
for i := range encInputs {

View File

@@ -172,12 +172,7 @@ func main() {
l.Printf("\tSetup done (cloud: %s, party: %s)\n",
elapsedRKGCloud+elapsedCKGCloud, elapsedRKGParty+elapsedCKGParty)
ringCtx := ring.NewContext()
ringCtx.SetParameters(params.N, params.Qi)
err = ringCtx.GenNTTParams()
if err != nil {
panic(err)
}
ringCtx := bfv.NewCiphertextRingContext(params)
// Pre-loading memory
l.Println("> Memory alloc Phase")