Skip to content

Commit

Permalink
applying lattigo-e patch
Browse files Browse the repository at this point in the history
CHANGELOG:
- Update of `PrecisionStats` in `ckks/precision.go`:
  - precision/error stats computed as log2 of min/max/average/...
  - fields renamed (`MinPrecision` -> `MINLog2Prec`, `MaxPrecision` -> `MAXLog2Prec`, ...)
  - `rlwe.Scale` has a `.Log2()` method
- Update of `mod1.Parameters` fields (made public, some removed)
- Improvement of the relinearization key-generation protocol (reduce the degree of the shares)
- Serialisation of bootstrapping keys
- Lower noise incurred by `ModUp`
- Evaluation keys can be compressed (public element `a` can be generated from a seed)
- More doc formatting
- Fix various bugs:
  - `ShallowCopy` of the CKKS bootstrapping evaluator and BFV evaluator not deep enough.
  - PSI example failing
  - Incorrect reset of pointer in uniform sampler
  - Error when doing inverse NTT with small degree
  - Mod1Evaluator changes the input ciphertext

Co-authored-by: Andrea Caforio <[email protected]>
Co-authored-by: Jean-Philippe Bossuat <[email protected]>
  • Loading branch information
3 people authored and lehugueni committed Oct 7, 2024
1 parent f8b84f5 commit e9e04b1
Show file tree
Hide file tree
Showing 42 changed files with 1,089 additions and 860 deletions.
4 changes: 2 additions & 2 deletions circuits/ckks/bootstrapping/bootstrapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ func verifyTestVectorsBootstrapping(params ckks.Parameters, encoder *ckks.Encode
t.Log(precStats.String())
}

rf64, _ := precStats.MeanPrecision.Real.Float64()
if64, _ := precStats.MeanPrecision.Imag.Float64()
rf64 := precStats.AVGLog2Prec.Real
if64 := precStats.AVGLog2Prec.Imag

minPrec := math.Log2(params.DefaultScale().Float64()) - float64(params.LogN()+2)
if minPrec < 0 {
Expand Down
79 changes: 56 additions & 23 deletions circuits/ckks/bootstrapping/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,19 @@ func (eval Evaluator) checkKeys(evk *EvaluationKeys) (err error) {
}

func (eval *Evaluator) initialize(btpParams Parameters) (err error) {

eval.Parameters = btpParams
params := btpParams.BootstrappingParameters

if eval.Mod1Parameters, err = mod1.NewParametersFromLiteral(params, btpParams.Mod1ParametersLiteral); err != nil {
return
}

scFac := eval.Mod1Parameters.ScFac()
K := eval.Mod1Parameters.K() / scFac
// [-K, K]
K := eval.Mod1Parameters.K

// Correcting factor for approximate division by Q
// The second correcting factor for approximate multiplication by Q is included in the coefficients of the EvalMod polynomials
qDiff := eval.Mod1Parameters.QDiff()
qDiff := eval.Mod1Parameters.QDiff

// If the scale used during the EvalMod step is smaller than Q0, then we cannot increase the scale during
// the EvalMod step to get a free division by MessageRatio, and we need to do this division (totally or partly)
Expand All @@ -198,19 +197,19 @@ func (eval *Evaluator) initialize(btpParams Parameters) (err error) {
scale := eval.BootstrappingParameters.DefaultScale().Float64()
offset := eval.Mod1Parameters.ScalingFactor().Float64() / eval.Mod1Parameters.MessageRatio()

C2SScaling := new(big.Float).SetFloat64(qDiv / (K * scFac * qDiff))
C2SScaling := new(big.Float).SetFloat64(qDiv / (K * qDiff))
StCScaling := new(big.Float).SetFloat64(scale / offset)

if eval.CoeffsToSlotsParameters.Scaling == nil {
if btpParams.CoeffsToSlotsParameters.Scaling == nil {
eval.CoeffsToSlotsParameters.Scaling = C2SScaling
} else {
eval.CoeffsToSlotsParameters.Scaling.Mul(eval.CoeffsToSlotsParameters.Scaling, C2SScaling)
eval.CoeffsToSlotsParameters.Scaling = new(big.Float).Mul(btpParams.CoeffsToSlotsParameters.Scaling, C2SScaling)
}

if eval.SlotsToCoeffsParameters.Scaling == nil {
if btpParams.SlotsToCoeffsParameters.Scaling == nil {
eval.SlotsToCoeffsParameters.Scaling = StCScaling
} else {
eval.SlotsToCoeffsParameters.Scaling.Mul(eval.SlotsToCoeffsParameters.Scaling, StCScaling)
eval.SlotsToCoeffsParameters.Scaling = new(big.Float).Mul(btpParams.SlotsToCoeffsParameters.Scaling, StCScaling)
}

if eval.C2SDFTMatrix, err = dft.NewMatrixFromLiteral(params, eval.CoeffsToSlotsParameters, encoder); err != nil {
Expand Down Expand Up @@ -319,11 +318,11 @@ func (eval Evaluator) MinimumInputLevel() int {
// can be used to do a scale matching.
//
// The circuit consists in 5 steps.
// 1) ScaleDown: scales the ciphertext to q/|m| and bringing it down to q
// 2) ModUp: brings the modulus from q to Q
// 3) CoeffsToSlots: homomorphic encoding
// 4) EvalMod: homomorphic modular reduction
// 5) SlotsToCoeffs: homomorphic decoding
// 1. ScaleDown: scales the ciphertext to q/|m| and bringing it down to q
// 2. ModUp: brings the modulus from q to Q
// 3. CoeffsToSlots: homomorphic encoding
// 4. EvalMod: homomorphic modular reduction
// 5. SlotsToCoeffs: homomorphic decoding
func (eval Evaluator) Evaluate(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext, err error) {

if eval.IterationsParameters == nil && eval.ResidualParameters.PrecisionMode() != ckks.PREC128 {
Expand Down Expand Up @@ -570,8 +569,9 @@ func (eval Evaluator) bootstrap(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext,

// ScaleDown brings the ciphertext level to zero and scaling factor to Q[0]/MessageRatio
// It multiplies the ciphertexts by round(currentMessageRatio / targetMessageRatio) where:
// - currentMessageRatio = Q/ctIn.Scale
// - targetMessageRatio = q/|m|
// - currentMessageRatio = Q/ctIn.Scale
// - targetMessageRatio = q/|m|
//
// and updates the scale of ctIn accordingly
// It then rescales the ciphertext down to q if necessary and also returns the rescaling error from this process
func (eval Evaluator) ScaleDown(ctIn *rlwe.Ciphertext) (*rlwe.Ciphertext, *rlwe.Scale, error) {
Expand Down Expand Up @@ -712,6 +712,24 @@ func (eval Evaluator) ModUp(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext, err

ringQ.NTT(ctIn.Value[0], ctIn.Value[0])

// Scale the message from Q0/|m| to QL/|m|, where QL is the largest modulus used during the bootstrapping.
if scale := (eval.Mod1Parameters.ScalingFactor().Float64() / eval.Mod1Parameters.MessageRatio()) / ctIn.Scale.Float64(); scale > 1 {

scalar := uint64(math.Round(scale))

for i := len(ks.BuffDecompQP) - 1; i >= 0; i-- {
ringQ.MulScalar(ks.BuffDecompQP[0].Q, scalar, ks.BuffDecompQP[i].Q)
}

for i := len(ks.BuffDecompQP) - 1; i >= 0; i-- {
ringP.MulScalar(ks.BuffDecompQP[0].P, scalar, ks.BuffDecompQP[i].P)
}

ringQ.MulScalar(ctIn.Value[0], scalar, ctIn.Value[0])

ctIn.Scale = ctIn.Scale.Mul(rlwe.NewScale(scale))
}

ctTmp := &rlwe.Ciphertext{}
ctTmp.Value = []ring.Poly{ks.BuffQP[1].Q, ctIn.Value[1]}
ctTmp.MetaData = ctIn.MetaData
Expand Down Expand Up @@ -739,17 +757,21 @@ func (eval Evaluator) ModUp(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext, err

ringQ.NTT(ctIn.Value[0], ctIn.Value[0])
ringQ.NTT(ctIn.Value[1], ctIn.Value[1])
}

// Scale the message from Q0/|m| to QL/|m|, where QL is the largest modulus used during the bootstrapping.
if scale := (eval.Mod1Parameters.ScalingFactor().Float64() / eval.Mod1Parameters.MessageRatio()) / ctIn.Scale.Float64(); scale > 1 {
if err = eval.ScaleUp(ctIn, rlwe.NewScale(scale), ctIn); err != nil {
return nil, err
// Scale the message from Q0/|m| to QL/|m|, where QL is the largest modulus used during the bootstrapping.
if scale := (eval.Mod1Parameters.ScalingFactor().Float64() / eval.Mod1Parameters.MessageRatio()) / ctIn.Scale.Float64(); scale > 1 {

scalar := uint64(math.Round(scale))

ringQ.MulScalar(ctIn.Value[0], scalar, ctIn.Value[0])
ringQ.MulScalar(ctIn.Value[1], scalar, ctIn.Value[1])

ctIn.Scale = ctIn.Scale.Mul(rlwe.NewScale(scale))
}
}

//SubSum X -> (N/dslots) * Y^dslots
return ctIn, eval.Trace(ctIn, ctIn.LogDimensions.Cols, ctIn)
return ctIn, eval.Trace(ctIn, eval.CoeffsToSlotsParameters.LogSlots, ctIn)
}

// CoeffsToSlots applies the homomorphic decoding
Expand All @@ -759,10 +781,21 @@ func (eval Evaluator) CoeffsToSlots(ctIn *rlwe.Ciphertext) (ctReal, ctImag *rlwe

// EvalMod applies the homomorphic modular reduction by q.
func (eval Evaluator) EvalMod(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext, err error) {

if ctOut, err = eval.Mod1Evaluator.EvaluateNew(ctIn); err != nil {
return nil, err
}

ctOut.Scale = eval.BootstrappingParameters.DefaultScale()
return
}

// EvalModAndScale applies the homomorphic modular reduction by q and scales the output value (without
// consumming an additional level).
func (eval Evaluator) EvalModAndScale(ctIn *rlwe.Ciphertext, scaling complex128) (ctOut *rlwe.Ciphertext, err error) {
if ctOut, err = eval.Mod1Evaluator.EvaluateAndScaleNew(ctIn, scaling); err != nil {
return nil, err
}

ctOut.Scale = eval.BootstrappingParameters.DefaultScale()
return
}
Expand Down
4 changes: 2 additions & 2 deletions circuits/ckks/bootstrapping/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ func verifyTestVectors(params ckks.Parameters, encoder *ckks.Encoder, decryptor
t.Log(precStats.String())
}

rf64, _ := precStats.MeanPrecision.Real.Float64()
if64, _ := precStats.MeanPrecision.Imag.Float64()
rf64 := precStats.AVGLog2Prec.Real
if64 := precStats.AVGLog2Prec.Imag

require.GreaterOrEqual(t, rf64, minPrec)
require.GreaterOrEqual(t, if64, minPrec)
Expand Down
Loading

0 comments on commit e9e04b1

Please sign in to comment.