mirror of
https://github.com/tuneinsight/lattigo.git
synced 2025-09-13 03:27:14 +00:00
[circuits/float]: rework of x mod 1
This commit is contained in:
@@ -15,7 +15,7 @@ import (
|
||||
type Bootstrapper struct {
|
||||
*ckks.Evaluator
|
||||
*float.DFTEvaluator
|
||||
*float.HModEvaluator
|
||||
*float.Mod1Evaluator
|
||||
*bootstrapperBase
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ type bootstrapperBase struct {
|
||||
dslots int // Number of plaintext slots after the re-encoding
|
||||
logdslots int
|
||||
|
||||
evalModPoly float.EvalModPoly
|
||||
stcMatrices float.DFTMatrix
|
||||
ctsMatrices float.DFTMatrix
|
||||
mod1Parameters float.Mod1Parameters
|
||||
stcMatrices float.DFTMatrix
|
||||
ctsMatrices float.DFTMatrix
|
||||
|
||||
q0OverMessageRatio float64
|
||||
}
|
||||
@@ -45,19 +45,19 @@ type EvaluationKeySet struct {
|
||||
// NewBootstrapper creates a new Bootstrapper.
|
||||
func NewBootstrapper(params ckks.Parameters, btpParams Parameters, btpKeys *EvaluationKeySet) (btp *Bootstrapper, err error) {
|
||||
|
||||
if btpParams.EvalModParameters.SineType == float.SinContinuous && btpParams.EvalModParameters.DoubleAngle != 0 {
|
||||
if btpParams.Mod1ParametersLiteral.SineType == float.SinContinuous && btpParams.Mod1ParametersLiteral.DoubleAngle != 0 {
|
||||
return nil, fmt.Errorf("cannot use double angle formul for SineType = Sin -> must use SineType = Cos")
|
||||
}
|
||||
|
||||
if btpParams.EvalModParameters.SineType == float.CosDiscrete && btpParams.EvalModParameters.SineDegree < 2*(btpParams.EvalModParameters.K-1) {
|
||||
if btpParams.Mod1ParametersLiteral.SineType == float.CosDiscrete && btpParams.Mod1ParametersLiteral.SineDegree < 2*(btpParams.Mod1ParametersLiteral.K-1) {
|
||||
return nil, fmt.Errorf("SineType 'ckks.CosDiscrete' uses a minimum degree of 2*(K-1) but EvalMod degree is smaller")
|
||||
}
|
||||
|
||||
if btpParams.CoeffsToSlotsParameters.LevelStart-btpParams.CoeffsToSlotsParameters.Depth(true) != btpParams.EvalModParameters.LevelStart {
|
||||
if btpParams.CoeffsToSlotsParameters.LevelStart-btpParams.CoeffsToSlotsParameters.Depth(true) != btpParams.Mod1ParametersLiteral.LevelStart {
|
||||
return nil, fmt.Errorf("starting level and depth of CoeffsToSlotsParameters inconsistent starting level of SineEvalParameters")
|
||||
}
|
||||
|
||||
if btpParams.EvalModParameters.LevelStart-btpParams.EvalModParameters.Depth() != btpParams.SlotsToCoeffsParameters.LevelStart {
|
||||
if btpParams.Mod1ParametersLiteral.LevelStart-btpParams.Mod1ParametersLiteral.Depth() != btpParams.SlotsToCoeffsParameters.LevelStart {
|
||||
return nil, fmt.Errorf("starting level and depth of SineEvalParameters inconsistent starting level of CoeffsToSlotsParameters")
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func NewBootstrapper(params ckks.Parameters, btpParams Parameters, btpKeys *Eval
|
||||
|
||||
btp.DFTEvaluator = float.NewDFTEvaluator(params, btp.Evaluator)
|
||||
|
||||
btp.HModEvaluator = float.NewHModEvaluator(btp.Evaluator)
|
||||
btp.Mod1Evaluator = float.NewMod1Evaluator(btp.Evaluator, btp.bootstrapperBase.mod1Parameters)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -168,26 +168,26 @@ func newBootstrapperBase(params ckks.Parameters, btpParams Parameters, btpKey *E
|
||||
bb.logdslots++
|
||||
}
|
||||
|
||||
if bb.evalModPoly, err = float.NewEvalModPolyFromLiteral(params, btpParams.EvalModParameters); err != nil {
|
||||
if bb.mod1Parameters, err = float.NewMod1ParametersFromLiteral(params, btpParams.Mod1ParametersLiteral); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scFac := bb.evalModPoly.ScFac()
|
||||
K := bb.evalModPoly.K() / scFac
|
||||
scFac := bb.mod1Parameters.ScFac()
|
||||
K := bb.mod1Parameters.K() / scFac
|
||||
|
||||
// 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 := bb.evalModPoly.QDiff()
|
||||
qDiff := bb.mod1Parameters.QDiff()
|
||||
|
||||
Q0 := params.Q()[0]
|
||||
|
||||
// Q0/|m|
|
||||
bb.q0OverMessageRatio = math.Exp2(math.Round(math.Log2(float64(Q0) / bb.evalModPoly.MessageRatio())))
|
||||
bb.q0OverMessageRatio = math.Exp2(math.Round(math.Log2(float64(Q0) / bb.mod1Parameters.MessageRatio())))
|
||||
|
||||
// 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)
|
||||
// during the CoeffstoSlots step
|
||||
qDiv := bb.evalModPoly.ScalingFactor().Float64() / math.Exp2(math.Round(math.Log2(float64(Q0))))
|
||||
qDiv := bb.mod1Parameters.ScalingFactor().Float64() / math.Exp2(math.Round(math.Log2(float64(Q0))))
|
||||
|
||||
// Sets qDiv to 1 if there is enough room for the division to happen using scale manipulation.
|
||||
if qDiv > 1 {
|
||||
@@ -213,9 +213,9 @@ func newBootstrapperBase(params ckks.Parameters, btpParams Parameters, btpKey *E
|
||||
// Rescaling factor to set the final ciphertext to the desired scale
|
||||
|
||||
if bb.SlotsToCoeffsParameters.Scaling == nil {
|
||||
bb.SlotsToCoeffsParameters.Scaling = new(big.Float).SetFloat64(bb.params.DefaultScale().Float64() / (bb.evalModPoly.ScalingFactor().Float64() / bb.evalModPoly.MessageRatio()) * qDiff)
|
||||
bb.SlotsToCoeffsParameters.Scaling = new(big.Float).SetFloat64(bb.params.DefaultScale().Float64() / (bb.mod1Parameters.ScalingFactor().Float64() / bb.mod1Parameters.MessageRatio()) * qDiff)
|
||||
} else {
|
||||
bb.SlotsToCoeffsParameters.Scaling.Mul(bb.SlotsToCoeffsParameters.Scaling, new(big.Float).SetFloat64(bb.params.DefaultScale().Float64()/(bb.evalModPoly.ScalingFactor().Float64()/bb.evalModPoly.MessageRatio())*qDiff))
|
||||
bb.SlotsToCoeffsParameters.Scaling.Mul(bb.SlotsToCoeffsParameters.Scaling, new(big.Float).SetFloat64(bb.params.DefaultScale().Float64()/(bb.mod1Parameters.ScalingFactor().Float64()/bb.mod1Parameters.MessageRatio())*qDiff))
|
||||
}
|
||||
|
||||
if bb.stcMatrices, err = float.NewDFTMatrixFromLiteral(params, bb.SlotsToCoeffsParameters, encoder); err != nil {
|
||||
|
||||
@@ -43,7 +43,7 @@ func (btp *Bootstrapper) Bootstrap(ctIn *rlwe.Ciphertext) (opOut *rlwe.Ciphertex
|
||||
|
||||
// Does an integer constant mult by round((Q0/Delta_m)/ctscale)
|
||||
if scale := ctDiff.Scale.Float64(); scale != math.Exp2(math.Round(math.Log2(scale))) || btp.q0OverMessageRatio < scale {
|
||||
msgRatio := btp.EvalModParameters.LogMessageRatio
|
||||
msgRatio := btp.Mod1ParametersLiteral.LogMessageRatio
|
||||
return nil, fmt.Errorf("cannot Bootstrap: ciphertext scale must be a power of two smaller than Q[0]/2^{LogMessageRatio=%d} = %f but is %f", msgRatio, float64(btp.params.Q()[0])/math.Exp2(float64(msgRatio)), scale)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (btp *Bootstrapper) Bootstrap(ctIn *rlwe.Ciphertext) (opOut *rlwe.Ciphertex
|
||||
}
|
||||
|
||||
// Scales the message to Q0/|m|, which is the maximum possible before ModRaise to avoid plaintext overflow.
|
||||
if scale := math.Round((float64(btp.params.Q()[0]) / btp.evalModPoly.MessageRatio()) / ctDiff.Scale.Float64()); scale > 1 {
|
||||
if scale := math.Round((float64(btp.params.Q()[0]) / btp.mod1Parameters.MessageRatio()) / ctDiff.Scale.Float64()); scale > 1 {
|
||||
if err = btp.ScaleUp(ctDiff, rlwe.NewScale(scale), ctDiff); err != nil {
|
||||
return nil, fmt.Errorf("cannot Bootstrap: %w", err)
|
||||
}
|
||||
@@ -107,7 +107,7 @@ func (btp *Bootstrapper) bootstrap(ctIn *rlwe.Ciphertext) (opOut *rlwe.Ciphertex
|
||||
}
|
||||
|
||||
// Scale the message from Q0/|m| to QL/|m|, where QL is the largest modulus used during the bootstrapping.
|
||||
if scale := (btp.evalModPoly.ScalingFactor().Float64() / btp.evalModPoly.MessageRatio()) / opOut.Scale.Float64(); scale > 1 {
|
||||
if scale := (btp.mod1Parameters.ScalingFactor().Float64() / btp.mod1Parameters.MessageRatio()) / opOut.Scale.Float64(); scale > 1 {
|
||||
if err = btp.ScaleUp(opOut, rlwe.NewScale(scale), opOut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -128,13 +128,13 @@ func (btp *Bootstrapper) bootstrap(ctIn *rlwe.Ciphertext) (opOut *rlwe.Ciphertex
|
||||
// ctReal = Ecd(real)
|
||||
// ctImag = Ecd(imag)
|
||||
// If n < N/2 then ctReal = Ecd(real|imag)
|
||||
if ctReal, err = btp.EvalModNew(ctReal, btp.evalModPoly); err != nil {
|
||||
if ctReal, err = btp.Mod1Evaluator.EvaluateNew(ctReal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctReal.Scale = btp.params.DefaultScale()
|
||||
|
||||
if ctImag != nil {
|
||||
if ctImag, err = btp.EvalModNew(ctImag, btp.evalModPoly); err != nil {
|
||||
if ctImag, err = btp.Mod1Evaluator.EvaluateNew(ctImag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctImag.Scale = btp.params.DefaultScale()
|
||||
|
||||
@@ -32,7 +32,7 @@ func BenchmarkBootstrap(b *testing.B) {
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
bootstrappingScale := rlwe.NewScale(math.Exp2(math.Round(math.Log2(float64(btp.params.Q()[0]) / btp.evalModPoly.MessageRatio()))))
|
||||
bootstrappingScale := rlwe.NewScale(math.Exp2(math.Round(math.Log2(float64(btp.params.Q()[0]) / btp.mod1Parameters.MessageRatio()))))
|
||||
|
||||
b.StopTimer()
|
||||
ct := ckks.NewCiphertext(params, 1, 0)
|
||||
@@ -61,12 +61,12 @@ func BenchmarkBootstrap(b *testing.B) {
|
||||
|
||||
// Part 2 : SineEval
|
||||
t = time.Now()
|
||||
ct0, err = btp.EvalModNew(ct0, btp.evalModPoly)
|
||||
ct0, err = btp.Mod1Evaluator.EvaluateNew(ct0)
|
||||
require.NoError(b, err)
|
||||
ct0.Scale = btp.params.DefaultScale()
|
||||
|
||||
if ct1 != nil {
|
||||
ct1, err = btp.EvalModNew(ct1, btp.evalModPoly)
|
||||
ct1, err = btp.Mod1Evaluator.EvaluateNew(ct1)
|
||||
require.NoError(b, err)
|
||||
ct1.Scale = btp.params.DefaultScale()
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestBootstrap(t *testing.T) {
|
||||
// Insecure params for fast testing only
|
||||
if !*flagLongTest {
|
||||
// Corrects the message ratio to take into account the smaller number of slots and keep the same precision
|
||||
btpParams.EvalModParameters.LogMessageRatio += utils.Min(utils.Max(15-LogSlots, 0), 8)
|
||||
btpParams.Mod1ParametersLiteral.LogMessageRatio += utils.Min(utils.Max(15-LogSlots, 0), 8)
|
||||
}
|
||||
|
||||
if !encapsulation {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// Parameters is a struct for the default bootstrapping parameters
|
||||
type Parameters struct {
|
||||
SlotsToCoeffsParameters float.DFTMatrixLiteral
|
||||
EvalModParameters float.EvalModLiteral
|
||||
Mod1ParametersLiteral float.Mod1ParametersLiteral
|
||||
CoeffsToSlotsParameters float.DFTMatrixLiteral
|
||||
Iterations int
|
||||
EphemeralSecretWeight int // Hamming weight of the ephemeral secret. If 0, no ephemeral secret is used during the bootstrapping.
|
||||
@@ -97,7 +97,7 @@ func NewParametersFromLiteral(ckksLit ckks.ParametersLiteral, btpLit ParametersL
|
||||
return ckks.ParametersLiteral{}, Parameters{}, err
|
||||
}
|
||||
|
||||
EvalModParams := float.EvalModLiteral{
|
||||
Mod1ParametersLiteral := float.Mod1ParametersLiteral{
|
||||
LogScale: EvalModLogScale,
|
||||
SineType: SineType,
|
||||
SineDegree: SineDegree,
|
||||
@@ -113,7 +113,7 @@ func NewParametersFromLiteral(ckksLit ckks.ParametersLiteral, btpLit ParametersL
|
||||
}
|
||||
|
||||
// Coeffs To Slots params
|
||||
EvalModParams.LevelStart = S2CParams.LevelStart + EvalModParams.Depth()
|
||||
Mod1ParametersLiteral.LevelStart = S2CParams.LevelStart + Mod1ParametersLiteral.Depth()
|
||||
|
||||
CoeffsToSlotsLevels := make([]int, len(CoeffsToSlotsFactorizationDepthAndLogScales))
|
||||
for i := range CoeffsToSlotsLevels {
|
||||
@@ -124,7 +124,7 @@ func NewParametersFromLiteral(ckksLit ckks.ParametersLiteral, btpLit ParametersL
|
||||
Type: float.HomomorphicEncode,
|
||||
LogSlots: LogSlots,
|
||||
RepackImag2Real: true,
|
||||
LevelStart: EvalModParams.LevelStart + len(CoeffsToSlotsFactorizationDepthAndLogScales),
|
||||
LevelStart: Mod1ParametersLiteral.LevelStart + len(CoeffsToSlotsFactorizationDepthAndLogScales),
|
||||
LogBSGSRatio: 1,
|
||||
Levels: CoeffsToSlotsLevels,
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func NewParametersFromLiteral(ckksLit ckks.ParametersLiteral, btpLit ParametersL
|
||||
LogQ = append(LogQ, qi)
|
||||
}
|
||||
|
||||
for i := 0; i < EvalModParams.Depth(); i++ {
|
||||
for i := 0; i < Mod1ParametersLiteral.Depth(); i++ {
|
||||
LogQ = append(LogQ, EvalModLogScale)
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ func NewParametersFromLiteral(ckksLit ckks.ParametersLiteral, btpLit ParametersL
|
||||
Parameters{
|
||||
EphemeralSecretWeight: EphemeralSecretWeight,
|
||||
SlotsToCoeffsParameters: S2CParams,
|
||||
EvalModParameters: EvalModParams,
|
||||
Mod1ParametersLiteral: Mod1ParametersLiteral,
|
||||
CoeffsToSlotsParameters: C2SParams,
|
||||
Iterations: Iterations,
|
||||
}, nil
|
||||
@@ -199,7 +199,7 @@ func (p *Parameters) DepthCoeffsToSlots() (depth int) {
|
||||
|
||||
// DepthEvalMod returns the depth of the EvalMod step of the CKKS bootstrapping.
|
||||
func (p *Parameters) DepthEvalMod() (depth int) {
|
||||
return p.EvalModParameters.Depth()
|
||||
return p.Mod1ParametersLiteral.Depth()
|
||||
}
|
||||
|
||||
// DepthSlotsToCoeffs returns the depth of the Slots to Coeffs step of the CKKS bootstrapping.
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/tuneinsight/lattigo/v4/utils/bignum"
|
||||
)
|
||||
|
||||
// DFTEvaluatorInterface is an interface defining the set of methods required to instantiate a DFTEvaluator.
|
||||
type DFTEvaluatorInterface interface {
|
||||
rlwe.ParameterProvider
|
||||
circuits.EvaluatorForLinearTransformation
|
||||
@@ -25,7 +26,7 @@ type DFTEvaluatorInterface interface {
|
||||
Rescale(op0 *rlwe.Ciphertext, opOut *rlwe.Ciphertext) (err error)
|
||||
}
|
||||
|
||||
// DFTType is a type used to distinguish different linear transformations.
|
||||
// DFTType is a type used to distinguish between different discrete Fourier transformations.
|
||||
type DFTType int
|
||||
|
||||
// HomomorphicEncode (IDFT) and HomomorphicDecode (DFT) are two available linear transformations for homomorphic encoding and decoding.
|
||||
@@ -35,7 +36,7 @@ const (
|
||||
)
|
||||
|
||||
// DFTMatrix is a struct storing the factorized IDFT, DFT matrices, which are
|
||||
// used to hommorphically encode and decode a ciphertext respectively.
|
||||
// used to homomorphically encode and decode a ciphertext respectively.
|
||||
type DFTMatrix struct {
|
||||
DFTMatrixLiteral
|
||||
Matrices []LinearTransformation
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// EvaluatorForMinimaxCompositePolynomial defines a set of common and scheme agnostic method that are necessary to instantiate a MinimaxCompositePolynomialEvaluator.
|
||||
type EvaluatorForMinimaxCompositePolynomial interface {
|
||||
circuits.EvaluatorForPolynomialEvaluation
|
||||
circuits.EvaluatorForPolynomial
|
||||
circuits.Evaluator
|
||||
ConjugateNew(ct *rlwe.Ciphertext) (ctConj *rlwe.Ciphertext, err error)
|
||||
}
|
||||
|
||||
123
circuits/float/mod1_evaluator.go
Normal file
123
circuits/float/mod1_evaluator.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package float
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/tuneinsight/lattigo/v4/circuits"
|
||||
"github.com/tuneinsight/lattigo/v4/ckks"
|
||||
"github.com/tuneinsight/lattigo/v4/rlwe"
|
||||
)
|
||||
|
||||
type EvaluatorForMod1 interface {
|
||||
circuits.Evaluator
|
||||
circuits.EvaluatorForPolynomial
|
||||
DropLevel(*rlwe.Ciphertext, int)
|
||||
GetParameters() *ckks.Parameters
|
||||
}
|
||||
|
||||
type Mod1Evaluator struct {
|
||||
EvaluatorForMod1
|
||||
PolynomialEvaluator PolynomialEvaluator
|
||||
Mod1Parameters Mod1Parameters
|
||||
}
|
||||
|
||||
func NewMod1Evaluator(eval EvaluatorForMod1, Mod1Parameters Mod1Parameters) *Mod1Evaluator {
|
||||
return &Mod1Evaluator{EvaluatorForMod1: eval, PolynomialEvaluator: *NewPolynomialEvaluator(*eval.GetParameters(), eval), Mod1Parameters: Mod1Parameters}
|
||||
}
|
||||
|
||||
// EvaluateNew applies a homomorphic mod Q on a vector scaled by Delta, scaled down to mod 1 :
|
||||
//
|
||||
// 1. Delta * (Q/Delta * I(X) + m(X)) (Delta = scaling factor, I(X) integer poly, m(X) message)
|
||||
// 2. Delta * (I(X) + Delta/Q * m(X)) (divide by Q/Delta)
|
||||
// 3. Delta * (Delta/Q * m(X)) (x mod 1)
|
||||
// 4. Delta * (m(X)) (multiply back by Q/Delta)
|
||||
//
|
||||
// Since Q is not a power of two, but Delta is, then does an approximate division by the closest
|
||||
// power of two to Q instead. Hence, it assumes that the input plaintext is already scaled by
|
||||
// the correcting factor Q/2^{round(log(Q))}.
|
||||
//
|
||||
// !! Assumes that the input is normalized by 1/K for K the range of the approximation.
|
||||
//
|
||||
// Scaling back error correction by 2^{round(log(Q))}/Q afterward is included in the polynomial
|
||||
func (eval Mod1Evaluator) EvaluateNew(ct *rlwe.Ciphertext) (*rlwe.Ciphertext, error) {
|
||||
|
||||
var err error
|
||||
|
||||
evm := eval.Mod1Parameters
|
||||
|
||||
if ct.Level() < evm.LevelStart() {
|
||||
return nil, fmt.Errorf("cannot Evaluate: ct.Level() < Mod1Parameters.LevelStart")
|
||||
}
|
||||
|
||||
if ct.Level() > evm.LevelStart() {
|
||||
eval.DropLevel(ct, ct.Level()-evm.LevelStart())
|
||||
}
|
||||
|
||||
// Stores default scales
|
||||
prevScaleCt := ct.Scale
|
||||
|
||||
// Normalize the modular reduction to mod by 1 (division by Q)
|
||||
ct.Scale = evm.ScalingFactor()
|
||||
|
||||
// Compute the scales that the ciphertext should have before the double angle
|
||||
// formula such that after it it has the scale it had before the polynomial
|
||||
// evaluation
|
||||
|
||||
Qi := eval.GetParameters().Q()
|
||||
|
||||
targetScale := ct.Scale
|
||||
for i := 0; i < evm.doubleAngle; i++ {
|
||||
targetScale = targetScale.Mul(rlwe.NewScale(Qi[evm.levelStart-evm.sinePoly.Depth()-evm.doubleAngle+i+1]))
|
||||
targetScale.Value.Sqrt(&targetScale.Value)
|
||||
}
|
||||
|
||||
// Division by 1/2^r and change of variable for the Chebyshev evaluation
|
||||
if evm.sineType == CosDiscrete || evm.sineType == CosContinuous {
|
||||
offset := new(big.Float).Sub(&evm.sinePoly.B, &evm.sinePoly.A)
|
||||
offset.Mul(offset, new(big.Float).SetFloat64(evm.scFac))
|
||||
offset.Quo(new(big.Float).SetFloat64(-0.5), offset)
|
||||
|
||||
if err = eval.Add(ct, offset, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot Evaluate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Chebyshev evaluation
|
||||
if ct, err = eval.PolynomialEvaluator.Evaluate(ct, evm.sinePoly, rlwe.NewScale(targetScale)); err != nil {
|
||||
return nil, fmt.Errorf("cannot Evaluate: %w", err)
|
||||
}
|
||||
|
||||
// Double angle
|
||||
sqrt2pi := evm.sqrt2Pi
|
||||
for i := 0; i < evm.doubleAngle; i++ {
|
||||
sqrt2pi *= sqrt2pi
|
||||
|
||||
if err = eval.MulRelin(ct, ct, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot Evaluate: %w", err)
|
||||
}
|
||||
|
||||
if err = eval.Add(ct, ct, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot Evaluate: %w", err)
|
||||
}
|
||||
|
||||
if err = eval.Add(ct, -sqrt2pi, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot Evaluate: %w", err)
|
||||
}
|
||||
|
||||
if err = eval.Rescale(ct, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot Evaluate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ArcSine
|
||||
if evm.arcSinePoly != nil {
|
||||
if ct, err = eval.PolynomialEvaluator.Evaluate(ct, *evm.arcSinePoly, ct.Scale); err != nil {
|
||||
return nil, fmt.Errorf("cannot Evaluate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies back by q
|
||||
ct.Scale = prevScaleCt
|
||||
return ct, nil
|
||||
}
|
||||
@@ -18,21 +18,6 @@ import (
|
||||
// for the homomorphic modular reduction
|
||||
type SineType uint64
|
||||
|
||||
func sin2pi(x *big.Float) (y *big.Float) {
|
||||
y = new(big.Float).Set(x)
|
||||
y.Mul(y, new(big.Float).SetFloat64(2))
|
||||
y.Mul(y, bignum.Pi(x.Prec()))
|
||||
return bignum.Sin(y)
|
||||
}
|
||||
|
||||
func cos2pi(x *big.Float) (y *big.Float) {
|
||||
y = new(big.Float).Set(x)
|
||||
y.Mul(y, new(big.Float).SetFloat64(2))
|
||||
y.Mul(y, bignum.Pi(x.Prec()))
|
||||
y = bignum.Cos(y)
|
||||
return y
|
||||
}
|
||||
|
||||
// Sin and Cos are the two proposed functions for SineType.
|
||||
// These trigonometric functions offer a good approximation of the function x mod 1 when the values are close to the origin.
|
||||
const (
|
||||
@@ -41,13 +26,13 @@ const (
|
||||
CosContinuous = SineType(2) // Standard Chebyshev approximation of pow((1/2pi), 1/2^r) * cos(2pi(x-0.25)/2^r) on the full interval
|
||||
)
|
||||
|
||||
// EvalModLiteral a struct for the parameters of the EvalMod procedure.
|
||||
// The EvalMod procedure goal is to homomorphically evaluate a modular reduction by Q[0] (the first prime of the moduli chain) on the encrypted plaintext.
|
||||
// This struct is consumed by `NewEvalModPolyFromLiteral` to generate the `EvalModPoly` struct, which notably stores
|
||||
// Mod1ParametersLiteral a struct for the parameters of the mod 1 procedure.
|
||||
// The x mod 1 procedure goal is to homomorphically evaluate a modular reduction by Q[0] (the first prime of the moduli chain) on the encrypted plaintext.
|
||||
// This struct is consumed by `NewMod1ParametersLiteralFromLiteral` to generate the `Mod1ParametersLiteral` struct, which notably stores
|
||||
// the coefficient of the polynomial approximating the function x mod Q[0].
|
||||
type EvalModLiteral struct {
|
||||
LevelStart int // Starting level of EvalMod
|
||||
LogScale int // Log2 of the scaling factor used during EvalMod
|
||||
type Mod1ParametersLiteral struct {
|
||||
LevelStart int // Starting level of x mod 1
|
||||
LogScale int // Log2 of the scaling factor used during x mod 1
|
||||
SineType SineType // Chose between [Sin(2*pi*x)] or [cos(2*pi*x/r) with double angle formula]
|
||||
LogMessageRatio int // Log2 of the ratio between Q0 and m, i.e. Q[0]/|m|
|
||||
K int // K parameter (interpolation in the range -K to K)
|
||||
@@ -56,20 +41,37 @@ type EvalModLiteral struct {
|
||||
ArcSineDegree int // Degree of the Taylor arcsine composed with f(2*pi*x) (if zero then not used)
|
||||
}
|
||||
|
||||
// MarshalBinary returns a JSON representation of the the target EvalModLiteral struct on a slice of bytes.
|
||||
// MarshalBinary returns a JSON representation of the the target Mod1ParametersLiteral struct on a slice of bytes.
|
||||
// See `Marshal` from the `encoding/json` package.
|
||||
func (evm EvalModLiteral) MarshalBinary() (data []byte, err error) {
|
||||
func (evm Mod1ParametersLiteral) MarshalBinary() (data []byte, err error) {
|
||||
return json.Marshal(evm)
|
||||
}
|
||||
|
||||
// UnmarshalBinary reads a JSON representation on the target EvalModLiteral struct.
|
||||
// UnmarshalBinary reads a JSON representation on the target Mod1ParametersLiteral struct.
|
||||
// See `Unmarshal` from the `encoding/json` package.
|
||||
func (evm *EvalModLiteral) UnmarshalBinary(data []byte) (err error) {
|
||||
func (evm *Mod1ParametersLiteral) UnmarshalBinary(data []byte) (err error) {
|
||||
return json.Unmarshal(data, evm)
|
||||
}
|
||||
|
||||
// EvalModPoly is a struct storing the parameters and polynomials approximating the function x mod Q[0] (the first prime of the moduli chain).
|
||||
type EvalModPoly struct {
|
||||
// Depth returns the depth required to evaluate x mod 1.
|
||||
func (evm Mod1ParametersLiteral) Depth() (depth int) {
|
||||
|
||||
if evm.SineType == CosDiscrete { // this method requires a minimum degree of 2*K-1.
|
||||
depth += int(bits.Len64(uint64(utils.Max(evm.SineDegree, 2*evm.K-1))))
|
||||
} else {
|
||||
depth += int(bits.Len64(uint64(evm.SineDegree)))
|
||||
}
|
||||
|
||||
if evm.SineType != SinContinuous {
|
||||
depth += evm.DoubleAngle
|
||||
}
|
||||
|
||||
depth += int(bits.Len64(uint64(evm.ArcSineDegree)))
|
||||
return depth
|
||||
}
|
||||
|
||||
// Mod1Parameters is a struct storing the parameters and polynomials approximating the function x mod Q[0] (the first prime of the moduli chain).
|
||||
type Mod1Parameters struct {
|
||||
levelStart int
|
||||
LogDefaultScale int
|
||||
sineType SineType
|
||||
@@ -83,41 +85,40 @@ type EvalModPoly struct {
|
||||
k float64
|
||||
}
|
||||
|
||||
// LevelStart returns the starting level of the EvalMod.
|
||||
func (evp EvalModPoly) LevelStart() int {
|
||||
// LevelStart returns the starting level of the x mod 1.
|
||||
func (evp Mod1Parameters) LevelStart() int {
|
||||
return evp.levelStart
|
||||
}
|
||||
|
||||
// ScalingFactor returns scaling factor used during the EvalMod.
|
||||
func (evp EvalModPoly) ScalingFactor() rlwe.Scale {
|
||||
// ScalingFactor returns scaling factor used during the x mod 1.
|
||||
func (evp Mod1Parameters) ScalingFactor() rlwe.Scale {
|
||||
return rlwe.NewScale(math.Exp2(float64(evp.LogDefaultScale)))
|
||||
}
|
||||
|
||||
// ScFac returns 1/2^r where r is the number of double angle evaluation.
|
||||
func (evp EvalModPoly) ScFac() float64 {
|
||||
func (evp Mod1Parameters) ScFac() float64 {
|
||||
return evp.scFac
|
||||
}
|
||||
|
||||
// MessageRatio returns the pre-set ratio Q[0]/|m|.
|
||||
func (evp EvalModPoly) MessageRatio() float64 {
|
||||
func (evp Mod1Parameters) MessageRatio() float64 {
|
||||
return float64(uint(1 << evp.LogMessageRatio))
|
||||
}
|
||||
|
||||
// K return the sine approximation range.
|
||||
func (evp EvalModPoly) K() float64 {
|
||||
func (evp Mod1Parameters) K() float64 {
|
||||
return evp.k * evp.scFac
|
||||
}
|
||||
|
||||
// QDiff return Q[0]/ClosetPow2
|
||||
// This is the error introduced by the approximate division by Q[0].
|
||||
func (evp EvalModPoly) QDiff() float64 {
|
||||
func (evp Mod1Parameters) QDiff() float64 {
|
||||
return evp.qDiff
|
||||
}
|
||||
|
||||
// NewEvalModPolyFromLiteral generates an EvalModPoly struct from the EvalModLiteral struct.
|
||||
// The EvalModPoly struct is used by the `EvalModNew` method from the `Evaluator`, which
|
||||
// homomorphically evaluates x mod Q[0] (the first prime of the moduli chain) on the ciphertext.
|
||||
func NewEvalModPolyFromLiteral(params ckks.Parameters, evm EvalModLiteral) (EvalModPoly, error) {
|
||||
// NewMod1ParametersFromLiteral generates an Mod1Parameters struct from the Mod1ParametersLiteral struct.
|
||||
// The Mod1Parameters struct is to instantiates a Mod1Evaluator, which homomorphically evaluates x mod 1.
|
||||
func NewMod1ParametersFromLiteral(params ckks.Parameters, evm Mod1ParametersLiteral) (Mod1Parameters, error) {
|
||||
|
||||
var arcSinePoly *bignum.Polynomial
|
||||
var sinePoly bignum.Polynomial
|
||||
@@ -203,7 +204,7 @@ func NewEvalModPolyFromLiteral(params ckks.Parameters, evm EvalModLiteral) (Eval
|
||||
}
|
||||
|
||||
default:
|
||||
return EvalModPoly{}, fmt.Errorf("invalid SineType")
|
||||
return Mod1Parameters{}, fmt.Errorf("invalid SineType")
|
||||
}
|
||||
|
||||
sqrt2piBig := new(big.Float).SetFloat64(sqrt2pi)
|
||||
@@ -214,7 +215,7 @@ func NewEvalModPolyFromLiteral(params ckks.Parameters, evm EvalModLiteral) (Eval
|
||||
}
|
||||
}
|
||||
|
||||
return EvalModPoly{
|
||||
return Mod1Parameters{
|
||||
levelStart: evm.LevelStart,
|
||||
LogDefaultScale: evm.LogScale,
|
||||
sineType: evm.SineType,
|
||||
@@ -229,122 +230,17 @@ func NewEvalModPolyFromLiteral(params ckks.Parameters, evm EvalModLiteral) (Eval
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Depth returns the depth of the SineEval.
|
||||
func (evm EvalModLiteral) Depth() (depth int) {
|
||||
|
||||
if evm.SineType == CosDiscrete { // this method requires a minimum degree of 2*K-1.
|
||||
depth += int(bits.Len64(uint64(utils.Max(evm.SineDegree, 2*evm.K-1))))
|
||||
} else {
|
||||
depth += int(bits.Len64(uint64(evm.SineDegree)))
|
||||
}
|
||||
|
||||
if evm.SineType != SinContinuous {
|
||||
depth += evm.DoubleAngle
|
||||
}
|
||||
|
||||
depth += int(bits.Len64(uint64(evm.ArcSineDegree)))
|
||||
return depth
|
||||
func sin2pi(x *big.Float) (y *big.Float) {
|
||||
y = new(big.Float).Set(x)
|
||||
y.Mul(y, new(big.Float).SetFloat64(2))
|
||||
y.Mul(y, bignum.Pi(x.Prec()))
|
||||
return bignum.Sin(y)
|
||||
}
|
||||
|
||||
type HModEvaluator struct {
|
||||
*ckks.Evaluator
|
||||
PolynomialEvaluator
|
||||
}
|
||||
|
||||
func NewHModEvaluator(eval *ckks.Evaluator) *HModEvaluator {
|
||||
return &HModEvaluator{Evaluator: eval, PolynomialEvaluator: *NewPolynomialEvaluator(*eval.GetParameters(), eval)}
|
||||
}
|
||||
|
||||
// EvalModNew applies a homomorphic mod Q on a vector scaled by Delta, scaled down to mod 1 :
|
||||
//
|
||||
// 1. Delta * (Q/Delta * I(X) + m(X)) (Delta = scaling factor, I(X) integer poly, m(X) message)
|
||||
// 2. Delta * (I(X) + Delta/Q * m(X)) (divide by Q/Delta)
|
||||
// 3. Delta * (Delta/Q * m(X)) (x mod 1)
|
||||
// 4. Delta * (m(X)) (multiply back by Q/Delta)
|
||||
//
|
||||
// Since Q is not a power of two, but Delta is, then does an approximate division by the closest
|
||||
// power of two to Q instead. Hence, it assumes that the input plaintext is already scaled by
|
||||
// the correcting factor Q/2^{round(log(Q))}.
|
||||
//
|
||||
// !! Assumes that the input is normalized by 1/K for K the range of the approximation.
|
||||
//
|
||||
// Scaling back error correction by 2^{round(log(Q))}/Q afterward is included in the polynomial
|
||||
func (eval *HModEvaluator) EvalModNew(ct *rlwe.Ciphertext, evalModPoly EvalModPoly) (*rlwe.Ciphertext, error) {
|
||||
|
||||
var err error
|
||||
|
||||
if ct.Level() < evalModPoly.LevelStart() {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: ct.Level() < evalModPoly.LevelStart")
|
||||
}
|
||||
|
||||
if ct.Level() > evalModPoly.LevelStart() {
|
||||
eval.DropLevel(ct, ct.Level()-evalModPoly.LevelStart())
|
||||
}
|
||||
|
||||
// Stores default scales
|
||||
prevScaleCt := ct.Scale
|
||||
|
||||
// Normalize the modular reduction to mod by 1 (division by Q)
|
||||
ct.Scale = evalModPoly.ScalingFactor()
|
||||
|
||||
// Compute the scales that the ciphertext should have before the double angle
|
||||
// formula such that after it it has the scale it had before the polynomial
|
||||
// evaluation
|
||||
|
||||
Qi := eval.GetParameters().Q()
|
||||
|
||||
targetScale := ct.Scale
|
||||
for i := 0; i < evalModPoly.doubleAngle; i++ {
|
||||
targetScale = targetScale.Mul(rlwe.NewScale(Qi[evalModPoly.levelStart-evalModPoly.sinePoly.Depth()-evalModPoly.doubleAngle+i+1]))
|
||||
targetScale.Value.Sqrt(&targetScale.Value)
|
||||
}
|
||||
|
||||
// Division by 1/2^r and change of variable for the Chebyshev evaluation
|
||||
if evalModPoly.sineType == CosDiscrete || evalModPoly.sineType == CosContinuous {
|
||||
offset := new(big.Float).Sub(&evalModPoly.sinePoly.B, &evalModPoly.sinePoly.A)
|
||||
offset.Mul(offset, new(big.Float).SetFloat64(evalModPoly.scFac))
|
||||
offset.Quo(new(big.Float).SetFloat64(-0.5), offset)
|
||||
|
||||
if err = eval.Add(ct, offset, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Chebyshev evaluation
|
||||
if ct, err = eval.Evaluate(ct, evalModPoly.sinePoly, rlwe.NewScale(targetScale)); err != nil {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: %w", err)
|
||||
}
|
||||
|
||||
// Double angle
|
||||
sqrt2pi := evalModPoly.sqrt2Pi
|
||||
for i := 0; i < evalModPoly.doubleAngle; i++ {
|
||||
sqrt2pi *= sqrt2pi
|
||||
|
||||
if err = eval.MulRelin(ct, ct, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: %w", err)
|
||||
}
|
||||
|
||||
if err = eval.Add(ct, ct, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: %w", err)
|
||||
}
|
||||
|
||||
if err = eval.Add(ct, -sqrt2pi, ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: %w", err)
|
||||
}
|
||||
|
||||
if err = eval.RescaleTo(ct, rlwe.NewScale(targetScale), ct); err != nil {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ArcSine
|
||||
if evalModPoly.arcSinePoly != nil {
|
||||
if ct, err = eval.Evaluate(ct, *evalModPoly.arcSinePoly, ct.Scale); err != nil {
|
||||
return nil, fmt.Errorf("cannot EvalModNew: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies back by q
|
||||
ct.Scale = prevScaleCt
|
||||
return ct, nil
|
||||
func cos2pi(x *big.Float) (y *big.Float) {
|
||||
y = new(big.Float).Set(x)
|
||||
y.Mul(y, new(big.Float).SetFloat64(2))
|
||||
y.Mul(y, bignum.Pi(x.Prec()))
|
||||
y = bignum.Cos(y)
|
||||
return y
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/tuneinsight/lattigo/v4/utils/sampling"
|
||||
)
|
||||
|
||||
func TestHomomorphicMod(t *testing.T) {
|
||||
func TestMod1(t *testing.T) {
|
||||
var err error
|
||||
|
||||
if runtime.GOARCH == "wasm" {
|
||||
@@ -28,7 +28,7 @@ func TestHomomorphicMod(t *testing.T) {
|
||||
LogDefaultScale: 45,
|
||||
}
|
||||
|
||||
testEvalModMarshalling(t)
|
||||
testMod1Marhsalling(t)
|
||||
|
||||
var params ckks.Parameters
|
||||
if params, err = ckks.NewParametersFromLiteral(ParametersLiteral); err != nil {
|
||||
@@ -36,17 +36,17 @@ func TestHomomorphicMod(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testSet := range []func(params ckks.Parameters, t *testing.T){
|
||||
testEvalMod,
|
||||
testMod1,
|
||||
} {
|
||||
testSet(params, t)
|
||||
runtime.GC()
|
||||
}
|
||||
}
|
||||
|
||||
func testEvalModMarshalling(t *testing.T) {
|
||||
func testMod1Marhsalling(t *testing.T) {
|
||||
t.Run("Marshalling", func(t *testing.T) {
|
||||
|
||||
evm := EvalModLiteral{
|
||||
evm := Mod1ParametersLiteral{
|
||||
LevelStart: 12,
|
||||
SineType: SinContinuous,
|
||||
LogMessageRatio: 8,
|
||||
@@ -59,7 +59,7 @@ func testEvalModMarshalling(t *testing.T) {
|
||||
data, err := evm.MarshalBinary()
|
||||
assert.Nil(t, err)
|
||||
|
||||
evmNew := new(EvalModLiteral)
|
||||
evmNew := new(Mod1ParametersLiteral)
|
||||
if err := evmNew.UnmarshalBinary(data); err != nil {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func testEvalModMarshalling(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func testEvalMod(params ckks.Parameters, t *testing.T) {
|
||||
func testMod1(params ckks.Parameters, t *testing.T) {
|
||||
|
||||
kgen := ckks.NewKeyGenerator(params)
|
||||
sk := kgen.GenSecretKeyNew()
|
||||
@@ -76,11 +76,9 @@ func testEvalMod(params ckks.Parameters, t *testing.T) {
|
||||
dec := ckks.NewDecryptor(params, sk)
|
||||
eval := ckks.NewEvaluator(params, rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk)))
|
||||
|
||||
modEval := NewHModEvaluator(eval)
|
||||
|
||||
t.Run("SineContinuousWithArcSine", func(t *testing.T) {
|
||||
|
||||
evm := EvalModLiteral{
|
||||
evm := Mod1ParametersLiteral{
|
||||
LevelStart: 12,
|
||||
SineType: SinContinuous,
|
||||
LogMessageRatio: 8,
|
||||
@@ -90,14 +88,14 @@ func testEvalMod(params ckks.Parameters, t *testing.T) {
|
||||
LogScale: 60,
|
||||
}
|
||||
|
||||
values, ciphertext := evaluatexmod1(evm, params, ecd, enc, modEval, t)
|
||||
values, ciphertext := evaluateMod1(evm, params, ecd, enc, eval, t)
|
||||
|
||||
ckks.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), nil, *printPrecisionStats, t)
|
||||
})
|
||||
|
||||
t.Run("CosDiscrete", func(t *testing.T) {
|
||||
|
||||
evm := EvalModLiteral{
|
||||
evm := Mod1ParametersLiteral{
|
||||
LevelStart: 12,
|
||||
SineType: CosDiscrete,
|
||||
LogMessageRatio: 8,
|
||||
@@ -107,14 +105,14 @@ func testEvalMod(params ckks.Parameters, t *testing.T) {
|
||||
LogScale: 60,
|
||||
}
|
||||
|
||||
values, ciphertext := evaluatexmod1(evm, params, ecd, enc, modEval, t)
|
||||
values, ciphertext := evaluateMod1(evm, params, ecd, enc, eval, t)
|
||||
|
||||
ckks.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), nil, *printPrecisionStats, t)
|
||||
})
|
||||
|
||||
t.Run("CosContinuous", func(t *testing.T) {
|
||||
|
||||
evm := EvalModLiteral{
|
||||
evm := Mod1ParametersLiteral{
|
||||
LevelStart: 12,
|
||||
SineType: CosContinuous,
|
||||
LogMessageRatio: 4,
|
||||
@@ -124,51 +122,51 @@ func testEvalMod(params ckks.Parameters, t *testing.T) {
|
||||
LogScale: 60,
|
||||
}
|
||||
|
||||
values, ciphertext := evaluatexmod1(evm, params, ecd, enc, modEval, t)
|
||||
values, ciphertext := evaluateMod1(evm, params, ecd, enc, eval, t)
|
||||
|
||||
ckks.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), nil, *printPrecisionStats, t)
|
||||
})
|
||||
}
|
||||
|
||||
func evaluatexmod1(evm EvalModLiteral, params ckks.Parameters, ecd *ckks.Encoder, enc *rlwe.Encryptor, eval *HModEvaluator, t *testing.T) ([]float64, *rlwe.Ciphertext) {
|
||||
func evaluateMod1(evm Mod1ParametersLiteral, params ckks.Parameters, ecd *ckks.Encoder, enc *rlwe.Encryptor, eval *ckks.Evaluator, t *testing.T) ([]float64, *rlwe.Ciphertext) {
|
||||
|
||||
EvalModPoly, err := NewEvalModPolyFromLiteral(params, evm)
|
||||
mod1Parameters, err := NewMod1ParametersFromLiteral(params, evm)
|
||||
require.NoError(t, err)
|
||||
|
||||
values, _, ciphertext := newTestVectorsEvalMod(params, enc, ecd, EvalModPoly, t)
|
||||
values, _, ciphertext := newTestVectorsMod1(params, enc, ecd, mod1Parameters, t)
|
||||
|
||||
// Scale the message to Delta = Q/MessageRatio
|
||||
scale := rlwe.NewScale(math.Exp2(math.Round(math.Log2(float64(params.Q()[0]) / EvalModPoly.MessageRatio()))))
|
||||
scale := rlwe.NewScale(math.Exp2(math.Round(math.Log2(float64(params.Q()[0]) / mod1Parameters.MessageRatio()))))
|
||||
scale = scale.Div(ciphertext.Scale)
|
||||
eval.ScaleUp(ciphertext, rlwe.NewScale(math.Round(scale.Float64())), ciphertext)
|
||||
|
||||
// Scale the message up to Sine/MessageRatio
|
||||
scale = EvalModPoly.ScalingFactor().Div(ciphertext.Scale)
|
||||
scale = scale.Div(rlwe.NewScale(EvalModPoly.MessageRatio()))
|
||||
scale = mod1Parameters.ScalingFactor().Div(ciphertext.Scale)
|
||||
scale = scale.Div(rlwe.NewScale(mod1Parameters.MessageRatio()))
|
||||
eval.ScaleUp(ciphertext, rlwe.NewScale(math.Round(scale.Float64())), ciphertext)
|
||||
|
||||
// Normalization
|
||||
require.NoError(t, eval.Mul(ciphertext, 1/(float64(EvalModPoly.K())*EvalModPoly.QDiff()), ciphertext))
|
||||
require.NoError(t, eval.Mul(ciphertext, 1/(float64(mod1Parameters.K())*mod1Parameters.QDiff()), ciphertext))
|
||||
require.NoError(t, eval.Rescale(ciphertext, ciphertext))
|
||||
|
||||
// EvalMod
|
||||
ciphertext, err = eval.EvalModNew(ciphertext, EvalModPoly)
|
||||
ciphertext, err = NewMod1Evaluator(eval, mod1Parameters).EvaluateNew(ciphertext)
|
||||
require.NoError(t, err)
|
||||
|
||||
// PlaintextCircuit
|
||||
for i := range values {
|
||||
x := values[i]
|
||||
|
||||
x /= EvalModPoly.MessageRatio()
|
||||
x /= EvalModPoly.QDiff()
|
||||
x /= mod1Parameters.MessageRatio()
|
||||
x /= mod1Parameters.QDiff()
|
||||
x = math.Sin(6.28318530717958 * x)
|
||||
|
||||
if evm.ArcSineDegree > 0 {
|
||||
x = math.Asin(x)
|
||||
}
|
||||
|
||||
x *= EvalModPoly.MessageRatio()
|
||||
x *= EvalModPoly.QDiff()
|
||||
x *= mod1Parameters.MessageRatio()
|
||||
x *= mod1Parameters.QDiff()
|
||||
x /= 6.28318530717958
|
||||
|
||||
values[i] = x
|
||||
@@ -177,7 +175,7 @@ func evaluatexmod1(evm EvalModLiteral, params ckks.Parameters, ecd *ckks.Encoder
|
||||
return values, ciphertext
|
||||
}
|
||||
|
||||
func newTestVectorsEvalMod(params ckks.Parameters, encryptor *rlwe.Encryptor, encoder *ckks.Encoder, evm EvalModPoly, t *testing.T) (values []float64, plaintext *rlwe.Plaintext, ciphertext *rlwe.Ciphertext) {
|
||||
func newTestVectorsMod1(params ckks.Parameters, encryptor *rlwe.Encryptor, encoder *ckks.Encoder, evm Mod1Parameters, t *testing.T) (values []float64, plaintext *rlwe.Plaintext, ciphertext *rlwe.Ciphertext) {
|
||||
|
||||
logSlots := params.LogMaxDimensions().Cols
|
||||
|
||||
@@ -25,9 +25,9 @@ func NewPowerBasis(ct *rlwe.Ciphertext, basis bignum.Basis) circuits.PowerBasis
|
||||
}
|
||||
|
||||
// NewPolynomialEvaluator instantiates a new PolynomialEvaluator.
|
||||
func NewPolynomialEvaluator(params ckks.Parameters, eval circuits.EvaluatorForPolynomialEvaluation) *PolynomialEvaluator {
|
||||
func NewPolynomialEvaluator(params ckks.Parameters, eval circuits.EvaluatorForPolynomial) *PolynomialEvaluator {
|
||||
e := new(PolynomialEvaluator)
|
||||
e.PolynomialEvaluator = circuits.PolynomialEvaluator{EvaluatorForPolynomialEvaluation: eval, EvaluatorBuffers: eval.GetEvaluatorBuffer()}
|
||||
e.PolynomialEvaluator = circuits.PolynomialEvaluator{EvaluatorForPolynomial: eval, EvaluatorBuffers: eval.GetEvaluatorBuffer()}
|
||||
e.Parameters = params
|
||||
return e
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ func NewPolynomialEvaluator(params bgv.Parameters, eval *bgv.Evaluator, Invarian
|
||||
e := new(PolynomialEvaluator)
|
||||
|
||||
if InvariantTensoring {
|
||||
e.PolynomialEvaluator = circuits.PolynomialEvaluator{EvaluatorForPolynomialEvaluation: scaleInvariantEvaluator{eval}, EvaluatorBuffers: eval.GetEvaluatorBuffer()}
|
||||
e.PolynomialEvaluator = circuits.PolynomialEvaluator{EvaluatorForPolynomial: scaleInvariantEvaluator{eval}, EvaluatorBuffers: eval.GetEvaluatorBuffer()}
|
||||
} else {
|
||||
e.PolynomialEvaluator = circuits.PolynomialEvaluator{EvaluatorForPolynomialEvaluation: eval, EvaluatorBuffers: eval.GetEvaluatorBuffer()}
|
||||
e.PolynomialEvaluator = circuits.PolynomialEvaluator{EvaluatorForPolynomial: eval, EvaluatorBuffers: eval.GetEvaluatorBuffer()}
|
||||
}
|
||||
|
||||
e.InvariantTensoring = InvariantTensoring
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/tuneinsight/lattigo/v4/utils/bignum"
|
||||
)
|
||||
|
||||
// EvaluatorForPolynomialEvaluation defines a set of common and scheme agnostic method that are necessary to instantiate a PolynomialVectorEvaluator.
|
||||
type EvaluatorForPolynomialEvaluation interface {
|
||||
// EvaluatorForPolynomial defines a set of common and scheme agnostic method that are necessary to instantiate a PolynomialVectorEvaluator.
|
||||
type EvaluatorForPolynomial interface {
|
||||
rlwe.ParameterProvider
|
||||
Evaluator
|
||||
Encode(values interface{}, pt *rlwe.Plaintext) (err error)
|
||||
@@ -23,7 +23,7 @@ type PolynomialVectorEvaluator interface {
|
||||
|
||||
// PolynomialEvaluator is an evaluator used to evaluate polynomials on ciphertexts.
|
||||
type PolynomialEvaluator struct {
|
||||
EvaluatorForPolynomialEvaluation
|
||||
EvaluatorForPolynomial
|
||||
*rlwe.EvaluatorBuffers
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ func main() {
|
||||
|
||||
if *flagShort {
|
||||
// Corrects the message ratio to take into account the smaller number of slots and keep the same precision
|
||||
btpParams.EvalModParameters.LogMessageRatio += 3
|
||||
btpParams.Mod1ParametersLiteral.LogMessageRatio += 3
|
||||
}
|
||||
|
||||
// This generate ckks.Parameters, with the NTT tables and other pre-computations from the ckks.ParametersLiteral (which is only a template).
|
||||
|
||||
@@ -87,14 +87,14 @@ func NewRemez(p RemezParameters) (r *Remez) {
|
||||
r.Coeffs[i] = new(big.Float)
|
||||
}
|
||||
|
||||
r.extrempoints = make([]point, 2*r.Degree)
|
||||
r.extrempoints = make([]point, 3*r.Degree)
|
||||
|
||||
for i := range r.extrempoints {
|
||||
r.extrempoints[i].x = new(big.Float)
|
||||
r.extrempoints[i].y = new(big.Float)
|
||||
}
|
||||
|
||||
r.localExtrempoints = make([]point, 2*r.Degree)
|
||||
r.localExtrempoints = make([]point, 3*r.Degree)
|
||||
for i := range r.localExtrempoints {
|
||||
r.localExtrempoints[i].x = new(big.Float)
|
||||
r.localExtrempoints[i].y = new(big.Float)
|
||||
|
||||
Reference in New Issue
Block a user