From 806a3564d50bd5f9e32b1d24ed94ce41ceebb2c3 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bossuat Date: Fri, 11 Aug 2023 17:28:17 +0200 Subject: [PATCH] [circuits/float]: rework of x mod 1 --- circuits/float/bootstrapping/bootstrapper.go | 34 +-- circuits/float/bootstrapping/bootstrapping.go | 10 +- .../bootstrapping/bootstrapping_bench_test.go | 6 +- .../float/bootstrapping/bootstrapping_test.go | 2 +- circuits/float/bootstrapping/parameters.go | 14 +- circuits/float/dft.go | 5 +- .../minimax_composite_polynomial_evaluator.go | 2 +- circuits/float/mod1_evaluator.go | 123 ++++++++++ .../float/{xmod1.go => mod1_parameters.go} | 210 +++++------------- .../float/{xmod1_test.go => mod1_test.go} | 54 +++-- circuits/float/polynomial_evaluator.go | 4 +- circuits/integer/polynomial_evaluator.go | 4 +- circuits/polynomial_evaluator.go | 6 +- examples/ckks/bootstrapping/main.go | 2 +- utils/bignum/minimax_approximation.go | 4 +- 15 files changed, 249 insertions(+), 231 deletions(-) create mode 100644 circuits/float/mod1_evaluator.go rename circuits/float/{xmod1.go => mod1_parameters.go} (54%) rename circuits/float/{xmod1_test.go => mod1_test.go} (73%) diff --git a/circuits/float/bootstrapping/bootstrapper.go b/circuits/float/bootstrapping/bootstrapper.go index d62b395d..e7662072 100644 --- a/circuits/float/bootstrapping/bootstrapper.go +++ b/circuits/float/bootstrapping/bootstrapper.go @@ -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 { diff --git a/circuits/float/bootstrapping/bootstrapping.go b/circuits/float/bootstrapping/bootstrapping.go index 93542f45..0b5f5315 100644 --- a/circuits/float/bootstrapping/bootstrapping.go +++ b/circuits/float/bootstrapping/bootstrapping.go @@ -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() diff --git a/circuits/float/bootstrapping/bootstrapping_bench_test.go b/circuits/float/bootstrapping/bootstrapping_bench_test.go index 37a5dde2..23b81c19 100644 --- a/circuits/float/bootstrapping/bootstrapping_bench_test.go +++ b/circuits/float/bootstrapping/bootstrapping_bench_test.go @@ -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() } diff --git a/circuits/float/bootstrapping/bootstrapping_test.go b/circuits/float/bootstrapping/bootstrapping_test.go index 21eccdb3..9efa6ceb 100644 --- a/circuits/float/bootstrapping/bootstrapping_test.go +++ b/circuits/float/bootstrapping/bootstrapping_test.go @@ -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 { diff --git a/circuits/float/bootstrapping/parameters.go b/circuits/float/bootstrapping/parameters.go index b7cb8161..53184e87 100644 --- a/circuits/float/bootstrapping/parameters.go +++ b/circuits/float/bootstrapping/parameters.go @@ -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. diff --git a/circuits/float/dft.go b/circuits/float/dft.go index d6a93d4e..cfaa1017 100644 --- a/circuits/float/dft.go +++ b/circuits/float/dft.go @@ -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 diff --git a/circuits/float/minimax_composite_polynomial_evaluator.go b/circuits/float/minimax_composite_polynomial_evaluator.go index f92d4e91..630a55bd 100644 --- a/circuits/float/minimax_composite_polynomial_evaluator.go +++ b/circuits/float/minimax_composite_polynomial_evaluator.go @@ -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) } diff --git a/circuits/float/mod1_evaluator.go b/circuits/float/mod1_evaluator.go new file mode 100644 index 00000000..b7a5789c --- /dev/null +++ b/circuits/float/mod1_evaluator.go @@ -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 +} diff --git a/circuits/float/xmod1.go b/circuits/float/mod1_parameters.go similarity index 54% rename from circuits/float/xmod1.go rename to circuits/float/mod1_parameters.go index 6fac6706..2009df99 100644 --- a/circuits/float/xmod1.go +++ b/circuits/float/mod1_parameters.go @@ -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 } diff --git a/circuits/float/xmod1_test.go b/circuits/float/mod1_test.go similarity index 73% rename from circuits/float/xmod1_test.go rename to circuits/float/mod1_test.go index a5d5292c..631f0c68 100644 --- a/circuits/float/xmod1_test.go +++ b/circuits/float/mod1_test.go @@ -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 diff --git a/circuits/float/polynomial_evaluator.go b/circuits/float/polynomial_evaluator.go index bf230d1b..208a6736 100644 --- a/circuits/float/polynomial_evaluator.go +++ b/circuits/float/polynomial_evaluator.go @@ -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 } diff --git a/circuits/integer/polynomial_evaluator.go b/circuits/integer/polynomial_evaluator.go index 0e47d5b6..bf16d8b6 100644 --- a/circuits/integer/polynomial_evaluator.go +++ b/circuits/integer/polynomial_evaluator.go @@ -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 diff --git a/circuits/polynomial_evaluator.go b/circuits/polynomial_evaluator.go index 1a7d683c..ae666ddf 100644 --- a/circuits/polynomial_evaluator.go +++ b/circuits/polynomial_evaluator.go @@ -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 } diff --git a/examples/ckks/bootstrapping/main.go b/examples/ckks/bootstrapping/main.go index 839f217c..7b7a6036 100644 --- a/examples/ckks/bootstrapping/main.go +++ b/examples/ckks/bootstrapping/main.go @@ -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). diff --git a/utils/bignum/minimax_approximation.go b/utils/bignum/minimax_approximation.go index 51a1083c..41432e86 100644 --- a/utils/bignum/minimax_approximation.go +++ b/utils/bignum/minimax_approximation.go @@ -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)