diff --git a/README.md b/README.md index 6e1a16b1..97606516 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,12 @@ The library exposes the following packages: a.k.a. CKKS) scheme. It provides approximate arithmetic over the complex numbers (in its classic variant) and over the real numbers (in its conjugate-invariant variant). -- `lattigo/he`: Generic methods and interfaces for linear transformation and polynomial evaluation. +- `lattigo/he`: HE scheme agnostic interfaces and algorithms for linear transformation and polynomial evaluation. This package also contains the following sub-packages: - - `blindrotation`: Blind rotations (a.k.a lookup tables). - - `float`: Advanced arithmetic for CKKS. - - `bootstrapper`: Bootstrapping for CKKS. - - `integer`: Advanced arithmetic for BGV/BFV. + - `blindrotation`: Blind rotations (a.k.a Lookup Tables). + - `float`: Homomorphic Encryption for fixed-point approximate arithmetic over the reals/complexes. + - `bootstrapper`: Bootstrapping for fixed-point approximate arithmetic over the reals/complexes. + - `integer`: Homomorphic Encryption for modular arithmetic over the integers. - `lattigo/dbfv`, `lattigo/dbgv` and `lattigo/dckks`: Multiparty (a.k.a. distributed or threshold) versions of the BFV, BGV and CKKS schemes that enable secure multiparty computation solutions with diff --git a/SECURITY.md b/SECURITY.md index aa0be5a1..6d9c3a1e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,7 +11,7 @@ Classified as an _approximate decryption_ scheme, the CKKS scheme is secure as l This attack demonstrates that, when using an approximate homomorphic encryption scheme, the usual CPA security may not sufficient depending on the application setting. Many applications do not require to share the result with external parties and are not affected by this attack, but the ones that do must take the appropriate steps to ensure that no key-dependent information is leaked. A homomorphic encryption scheme that provides such functionality and that can be secure when releasing decrypted plaintext to external parties is defined to be CPAD secure. The corresponding indistinguishability notion (IND-CPAD) is defined as "indistinguishability under chosen plaintext attacks with decryption oracles." -# CPAD Security for CKKS +# CPAD Security for Approximate Homomorphic Encryption Lattigo implements tools to mitigate _Li and Micciancio_'s attack. In particular, the decoding step of CKKS (and its real-number variant R-CKKS) allows the user to specify the desired fixed-point bit-precision. Let $\epsilon$ be the scheme error after the decoding step. We compute the bit precision of the output as $\log_{2}(1/\epsilon)$. diff --git a/bfv/bfv_test.go b/bfv/bfv_test.go index 71527661..2d493d6c 100644 --- a/bfv/bfv_test.go +++ b/bfv/bfv_test.go @@ -176,13 +176,13 @@ func testParameters(tc *testContext, t *testing.T) { require.Nil(t, err) require.NotNil(t, data) - // checks that ckks.Parameters can be unmarshalled without error + // checks that the Parameters can be unmarshalled without error var paramsRec Parameters err = json.Unmarshal(data, ¶msRec) require.Nil(t, err) require.True(t, tc.params.Equal(¶msRec)) - // checks that ckks.Parameters can be unmarshalled with log-moduli definition without error + // checks that the Parameters can be unmarshalled with log-moduli definition without error dataWithLogModuli := []byte(fmt.Sprintf(`{"LogN":%d,"LogQ":[50,50],"LogP":[60], "PlaintextModulus":65537}`, tc.params.LogN())) var paramsWithLogModuli Parameters err = json.Unmarshal(dataWithLogModuli, ¶msWithLogModuli) diff --git a/bgv/bgv_test.go b/bgv/bgv_test.go index 88ce8f2d..ae6bd13d 100644 --- a/bgv/bgv_test.go +++ b/bgv/bgv_test.go @@ -188,13 +188,13 @@ func testParameters(tc *testContext, t *testing.T) { require.Nil(t, err) require.NotNil(t, data) - // checks that ckks.Parameters can be unmarshalled without error + // checks that the Parameters can be unmarshalled without error var paramsRec Parameters err = json.Unmarshal(data, ¶msRec) require.Nil(t, err) require.True(t, tc.params.Equal(¶msRec)) - // checks that ckks.Parameters can be unmarshalled with log-moduli definition without error + // checks that the Parameters can be unmarshalled with log-moduli definition without error dataWithLogModuli := []byte(fmt.Sprintf(`{"LogN":%d,"LogQ":[50,50],"LogP":[60], "PlaintextModulus":65537}`, tc.params.LogN())) var paramsWithLogModuli Parameters err = json.Unmarshal(dataWithLogModuli, ¶msWithLogModuli) diff --git a/examples/ckks/advanced/scheme_switching/main.go b/examples/hefloat/advanced/scheme_switching/main.go similarity index 79% rename from examples/ckks/advanced/scheme_switching/main.go rename to examples/hefloat/advanced/scheme_switching/main.go index 5c65e489..5326dbfc 100644 --- a/examples/ckks/advanced/scheme_switching/main.go +++ b/examples/hefloat/advanced/scheme_switching/main.go @@ -6,7 +6,6 @@ import ( "math/big" "time" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/blindrotation" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" @@ -14,11 +13,13 @@ import ( "github.com/tuneinsight/lattigo/v4/utils" ) -// This example showcases how lookup tables can complement the CKKS scheme to compute non-linear functions -// such as sign. The example starts by homomorphically decoding the CKKS ciphertext from the canonical embedding -// to the coefficient embedding. It then evaluates the Look-Up-Table (BlindRotation) on each coefficient and repacks the -// outputs of each Blind Rotation in a single RLWE ciphertext. Finally, it homomorphically encodes the RLWE ciphertext back -// to the canonical embedding of the CKKS scheme. +// This example showcases how lookup tables can complement fixed-point approximate +// homomorphic encryption to compute non-linear functions such as sign. +// The example starts by homomorphically decoding the ciphertext from the SIMD +// encoding to the coefficient encoding: IDFT(m(X)) -> m(X). +// It then evaluates a Lookup-Table (LUT) on each coefficient of m(X): m(X)[i] -> LUT(m(X)[i]) +// and repacks each LUT(m(X)[i]) in a single RLWE ciphertext: Repack(LUT(m(X)[i])) -> LUT(m(X)). +// Finally, it homomorphically switches LUT(m(X)) back to the SIMD domain: LUT(m(X)) -> IDFT(LUT(m(X))). // ======================================== // Functions to evaluate with BlindRotation @@ -60,8 +61,8 @@ func main() { // determine the complexity of the BlindRotation: // each BlindRotation takes ~N RGSW ciphertext-ciphertext mul. // LogN = 12 & LogQP = ~103 -> >128-bit secure. - var paramsN12 ckks.Parameters - if paramsN12, err = ckks.NewParametersFromLiteral(ckks.ParametersLiteral{ + var paramsN12 float.Parameters + if paramsN12, err = float.NewParametersFromLiteral(float.ParametersLiteral{ LogN: LogN, Q: Q, P: P, @@ -73,8 +74,8 @@ func main() { // BlindRotation RLWE params, N of these params determine // the test poly degree and therefore precision. // LogN = 11 & LogQP = ~54 -> 128-bit secure. - var paramsN11 ckks.Parameters - if paramsN11, err = ckks.NewParametersFromLiteral(ckks.ParametersLiteral{ + var paramsN11 float.Parameters + if paramsN11, err = float.NewParametersFromLiteral(float.ParametersLiteral{ LogN: LogN - 1, Q: Q[:1], P: []uint64{0x42001}, @@ -129,17 +130,17 @@ func main() { repackIndex[i*gapN11] = i * gapN12 } - kgenN12 := ckks.NewKeyGenerator(paramsN12) + kgenN12 := rlwe.NewKeyGenerator(paramsN12) skN12 := kgenN12.GenSecretKeyNew() - encoderN12 := ckks.NewEncoder(paramsN12) - encryptorN12 := ckks.NewEncryptor(paramsN12, skN12) - decryptorN12 := ckks.NewDecryptor(paramsN12, skN12) + encoderN12 := float.NewEncoder(paramsN12) + encryptorN12 := rlwe.NewEncryptor(paramsN12, skN12) + decryptorN12 := rlwe.NewDecryptor(paramsN12, skN12) - kgenN11 := ckks.NewKeyGenerator(paramsN11) + kgenN11 := rlwe.NewKeyGenerator(paramsN11) skN11 := kgenN11.GenSecretKeyNew() // EvaluationKey RLWEN12 -> RLWEN11 - evkN12ToN11 := ckks.NewKeyGenerator(paramsN12).GenEvaluationKeyNew(skN12, skN11) + evkN12ToN11 := rlwe.NewKeyGenerator(paramsN12).GenEvaluationKeyNew(skN12, skN11) fmt.Printf("Gen SlotsToCoeffs Matrices... ") now = time.Now() @@ -162,15 +163,15 @@ func main() { evk := rlwe.NewMemEvaluationKeySet(nil, kgenN12.GenGaloisKeysNew(galEls, skN12)...) // BlindRotation Evaluator - evalBR := blindrotation.NewEvaluator(paramsN12.Parameters, paramsN11.Parameters) + evalBR := blindrotation.NewEvaluator(paramsN12, paramsN11) - // CKKS Evaluator - evalCKKS := ckks.NewEvaluator(paramsN12, evk) - evalHDFT := float.NewDFTEvaluator(paramsN12, evalCKKS) + // Evaluator + eval := float.NewEvaluator(paramsN12, evk) + evalHDFT := float.NewDFTEvaluator(paramsN12, eval) fmt.Printf("Encrypting bits of skLWE in RGSW... ") now = time.Now() - blindRotateKey := blindrotation.GenEvaluationKeyNew(paramsN12.Parameters, skN12, paramsN11.Parameters, skN11, evkParams) // Generate RGSW(sk_i) for all coefficients of sk + blindRotateKey := blindrotation.GenEvaluationKeyNew(paramsN12, skN12, paramsN11, skN11, evkParams) // Generate RGSW(sk_i) for all coefficients of sk fmt.Printf("Done (%s)\n", time.Since(now)) // Generates the starting plaintext values. @@ -180,7 +181,7 @@ func main() { values[i] = a + float64(i)*interval } - pt := ckks.NewPlaintext(paramsN12, paramsN12.MaxLevel()) + pt := float.NewPlaintext(paramsN12, paramsN12.MaxLevel()) pt.LogDimensions.Cols = LogSlots if err := encoderN12.Encode(values, pt); err != nil { panic(err) @@ -202,9 +203,9 @@ func main() { ctN12.IsBatched = false // Key-Switch from LogN = 12 to LogN = 11 - ctN11 := ckks.NewCiphertext(paramsN11, 1, paramsN11.MaxLevel()) + ctN11 := float.NewCiphertext(paramsN11, 1, paramsN11.MaxLevel()) // key-switch to LWE degree - if err := evalCKKS.ApplyEvaluationKey(ctN12, evkN12ToN11, ctN11); err != nil { + if err := eval.ApplyEvaluationKey(ctN12, evkN12ToN11, ctN11); err != nil { panic(err) } fmt.Printf("Done (%s)\n", time.Since(now)) diff --git a/examples/ckks/advanced/scheme_switching/main_test.go b/examples/hefloat/advanced/scheme_switching/main_test.go similarity index 100% rename from examples/ckks/advanced/scheme_switching/main_test.go rename to examples/hefloat/advanced/scheme_switching/main_test.go diff --git a/examples/ckks/bootstrapping/basic/main.go b/examples/hefloat/bootstrapping/basic/main.go similarity index 87% rename from examples/ckks/bootstrapping/basic/main.go rename to examples/hefloat/bootstrapping/basic/main.go index 44fd28e5..1690ae4b 100644 --- a/examples/ckks/bootstrapping/basic/main.go +++ b/examples/hefloat/bootstrapping/basic/main.go @@ -1,4 +1,4 @@ -// Package main implements an example showcasing the basics of the bootstrapping for encrypted floating point numbers (CKKS). +// Package main implements an example showcasing the basics of the bootstrapping for fixed-point approximate arithmetic over the reals/complexes. // The bootstrapping is a circuit that homomorphically re-encrypts a ciphertext at level zero to a ciphertext at a higher level, enabling further computations. // Note that, unlike other bootstrappings (BGV/BFV/TFHE), the this bootstrapping does not reduce the error in the ciphertext, but only enables further computations. // This example shows how to bootstrap a single ciphertext whose ring degree is the same as the one of the bootstrapping parameters. @@ -10,7 +10,7 @@ import ( "fmt" "math" - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/he/float/bootstrapper" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -40,7 +40,7 @@ func main() { // The residual parameters are the parameters used outside of the bootstrapping circuit. // For this example, we have a LogN=16, logQ = 55 + 10*40 and logP = 3*61, so LogQP = 638. // With LogN=16, LogQP=638 and H=192, these parameters achieve well over 128-bit of security. - params, err := ckks.NewParametersFromLiteral(ckks.ParametersLiteral{ + params, err := float.NewParametersFromLiteral(float.ParametersLiteral{ LogN: LogN, // Log2 of the ringdegree LogQ: []int{55, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, // Log2 of the ciphertext prime moduli LogP: []int{61, 61, 61}, // Log2 of the key-switch auxiliary prime moduli @@ -59,7 +59,7 @@ func main() { // The bootstrapping circuit use its own Parameters which will be automatically // instantiated given the residual parameters and the bootstrapping parameters. - // !WARNING! The bootstrapping ckks parameters are not ensure to be 128-bit secure, it is the + // !WARNING! The bootstrapping parameters are not ensure to be 128-bit secure, it is the // responsibility of the user to check that the meet the security requirement and tweak them if necessary. // Note that the default bootstrapping parameters use LogN=16 and a ternary secret with H=192 non-zero coefficients @@ -89,10 +89,10 @@ func main() { // Now that the residual parameters and the bootstrapping parameters literals are defined, we can instantiate // the bootstrapping parameters. - // The instantiated bootstrapping parameters store their own ckks.Parameter, which are the parameters of the + // The instantiated bootstrapping parameters store their own float.Parameter, which are the parameters of the // ring used by the bootstrapping circuit. - // The bootstrapping parameters are a wrapper of ckks.Parameters, with additional information. - // They therefore has the same API as the ckks.Parameters and we can use this API to print some information. + // The bootstrapping parameters are a wrapper of float.Parameters, with additional information. + // They therefore has the same API as the float.Parameters and we can use this API to print some information. btpParams, err := bootstrapper.NewParametersFromLiteral(params, btpParametersLit) if err != nil { panic(err) @@ -133,13 +133,13 @@ func main() { // instantiate the usual necessary object to encode, encrypt and decrypt. // Scheme context and keys - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) sk, pk := kgen.GenKeyPairNew() - encoder := ckks.NewEncoder(params) - decryptor := ckks.NewDecryptor(params, sk) - encryptor := ckks.NewEncryptor(params, pk) + encoder := float.NewEncoder(params) + decryptor := rlwe.NewDecryptor(params, sk) + encryptor := rlwe.NewEncryptor(params, pk) fmt.Println() fmt.Println("Generating bootstrapping keys...") @@ -166,7 +166,7 @@ func main() { } // We encrypt at level 0 - plaintext := ckks.NewPlaintext(params, 0) + plaintext := float.NewPlaintext(params, 0) if err := encoder.Encode(valuesWant, plaintext); err != nil { panic(err) } @@ -184,7 +184,7 @@ func main() { // Bootstrap the ciphertext (homomorphic re-encryption) // It takes a ciphertext at level 0 (if not at level 0, then it will reduce it to level 0) - // and returns a ciphertext with the max level of `ckksParamsResidualLit`. + // and returns a ciphertext with the max level of `floatParamsResidualLit`. // CAUTION: the scale of the ciphertext MUST be equal (or very close) to params.DefaultScale() // To equalize the scale, the function evaluator.SetScale(ciphertext, parameters.DefaultScale()) can be used at the expense of one level. // If the ciphertext is is at level one or greater when given to the bootstrapper, this equalization is automatically done. @@ -205,7 +205,7 @@ func main() { printDebug(params, ciphertext2, valuesTest1, decryptor, encoder) } -func printDebug(params ckks.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []complex128, decryptor *rlwe.Decryptor, encoder *ckks.Encoder) (valuesTest []complex128) { +func printDebug(params float.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []complex128, decryptor *rlwe.Decryptor, encoder *float.Encoder) (valuesTest []complex128) { valuesTest = make([]complex128, ciphertext.Slots()) @@ -220,7 +220,7 @@ func printDebug(params ckks.Parameters, ciphertext *rlwe.Ciphertext, valuesWant fmt.Printf("ValuesTest: %6.10f %6.10f %6.10f %6.10f...\n", valuesTest[0], valuesTest[1], valuesTest[2], valuesTest[3]) fmt.Printf("ValuesWant: %6.10f %6.10f %6.10f %6.10f...\n", valuesWant[0], valuesWant[1], valuesWant[2], valuesWant[3]) - precStats := ckks.GetPrecisionStats(params, encoder, nil, valuesWant, valuesTest, 0, false) + precStats := float.GetPrecisionStats(params, encoder, nil, valuesWant, valuesTest, 0, false) fmt.Println(precStats.String()) fmt.Println() diff --git a/examples/ckks/bootstrapping/basic/main_test.go b/examples/hefloat/bootstrapping/basic/main_test.go similarity index 100% rename from examples/ckks/bootstrapping/basic/main_test.go rename to examples/hefloat/bootstrapping/basic/main_test.go diff --git a/examples/ckks/euler/main.go b/examples/hefloat/euler/main.go similarity index 85% rename from examples/ckks/euler/main.go rename to examples/hefloat/euler/main.go index b449757e..5f8f7a97 100644 --- a/examples/ckks/euler/main.go +++ b/examples/hefloat/euler/main.go @@ -6,7 +6,6 @@ import ( "math/cmplx" "time" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -19,8 +18,8 @@ func example() { var err error // Schemes parameters are created from scratch - params, err := ckks.NewParametersFromLiteral( - ckks.ParametersLiteral{ + params, err := float.NewParametersFromLiteral( + float.ParametersLiteral{ LogN: 14, LogQ: []int{55, 40, 40, 40, 40, 40, 40, 40}, LogP: []int{45, 45}, @@ -38,15 +37,15 @@ func example() { start = time.Now() - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) sk := kgen.GenSecretKeyNew() - encryptor := ckks.NewEncryptor(params, sk) - decryptor := ckks.NewDecryptor(params, sk) - encoder := ckks.NewEncoder(params) + encryptor := rlwe.NewEncryptor(params, sk) + decryptor := rlwe.NewDecryptor(params, sk) + encoder := float.NewEncoder(params) evk := rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk)) - evaluator := ckks.NewEvaluator(params, evk) + evaluator := float.NewEvaluator(params, evk) fmt.Printf("Done in %s \n", time.Since(start)) @@ -54,7 +53,7 @@ func example() { slots := 1 << logSlots fmt.Println() - fmt.Printf("CKKS parameters: logN = %d, logSlots = %d, logQP = %f, levels = %d, scale= %f, noise = %T %v \n", params.LogN(), logSlots, params.LogQP(), params.MaxLevel()+1, params.DefaultScale().Float64(), params.Xe(), params.Xe()) + fmt.Printf("Scheme parameters: logN = %d, logSlots = %d, logQP = %f, levels = %d, scale= %f, noise = %T %v \n", params.LogN(), logSlots, params.LogQP(), params.MaxLevel()+1, params.DefaultScale().Float64(), params.Xe(), params.Xe()) fmt.Println() fmt.Println("=========================================") @@ -73,7 +72,7 @@ func example() { values[i] = complex(2*pi, 0) } - plaintext := ckks.NewPlaintext(params, params.MaxLevel()) + plaintext := float.NewPlaintext(params, params.MaxLevel()) plaintext.Scale = plaintext.Scale.Div(rlwe.NewScale(r)) if err := encoder.Encode(values, plaintext); err != nil { panic(err) @@ -208,7 +207,7 @@ func example() { } -func printDebug(params ckks.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []complex128, decryptor *rlwe.Decryptor, encoder *ckks.Encoder) (valuesTest []complex128) { +func printDebug(params float.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []complex128, decryptor *rlwe.Decryptor, encoder *float.Encoder) (valuesTest []complex128) { valuesTest = make([]complex128, ciphertext.Slots()) @@ -223,7 +222,7 @@ func printDebug(params ckks.Parameters, ciphertext *rlwe.Ciphertext, valuesWant fmt.Printf("ValuesWant: %6.10f %6.10f %6.10f %6.10f...\n", valuesWant[0], valuesWant[1], valuesWant[2], valuesWant[3]) fmt.Println() - precStats := ckks.GetPrecisionStats(params, encoder, nil, valuesWant, valuesTest, 0, false) + precStats := float.GetPrecisionStats(params, encoder, nil, valuesWant, valuesTest, 0, false) fmt.Println(precStats.String()) diff --git a/examples/ckks/ckks_tutorial/main_test.go b/examples/hefloat/euler/main_test.go similarity index 100% rename from examples/ckks/ckks_tutorial/main_test.go rename to examples/hefloat/euler/main_test.go diff --git a/examples/ckks/polyeval/main.go b/examples/hefloat/polynomial/main.go similarity index 85% rename from examples/ckks/polyeval/main.go rename to examples/hefloat/polynomial/main.go index 1109c2bf..fd0f6734 100644 --- a/examples/ckks/polyeval/main.go +++ b/examples/hefloat/polynomial/main.go @@ -5,7 +5,6 @@ import ( "math" "math/big" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils/bignum" @@ -23,8 +22,8 @@ func chebyshevinterpolation() { // The result is then parsed and compared to the expected result. // Scheme params are taken directly from the proposed defaults - params, err := ckks.NewParametersFromLiteral( - ckks.ParametersLiteral{ + params, err := float.NewParametersFromLiteral( + float.ParametersLiteral{ LogN: 14, LogQ: []int{55, 40, 40, 40, 40, 40, 40, 40}, LogP: []int{45, 45}, @@ -34,20 +33,20 @@ func chebyshevinterpolation() { panic(err) } - encoder := ckks.NewEncoder(params) + encoder := float.NewEncoder(params) // Keys - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) sk, pk := kgen.GenKeyPairNew() // Encryptor - encryptor := ckks.NewEncryptor(params, pk) + encryptor := rlwe.NewEncryptor(params, pk) // Decryptor - decryptor := ckks.NewDecryptor(params, sk) + decryptor := rlwe.NewDecryptor(params, sk) // Evaluator with relinearization key - evaluator := ckks.NewEvaluator(params, rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk))) + evaluator := float.NewEvaluator(params, rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk))) // Values to encrypt slots := params.MaxSlots() @@ -56,7 +55,7 @@ func chebyshevinterpolation() { values[i] = sampling.RandFloat64(-8, 8) } - fmt.Printf("CKKS parameters: logN = %d, logQ = %f, levels = %d, scale= %f, noise = %T %v \n", + fmt.Printf("Scheme parameters: logN = %d, logQ = %f, levels = %d, scale= %f, noise = %T %v \n", params.LogN(), params.LogQP(), params.MaxLevel()+1, params.DefaultScale().Float64(), params.Xe(), params.Xe()) fmt.Println() @@ -65,7 +64,7 @@ func chebyshevinterpolation() { fmt.Println() // Plaintext creation and encoding process - plaintext := ckks.NewPlaintext(params, params.MaxLevel()) + plaintext := float.NewPlaintext(params, params.MaxLevel()) if err := encoder.Encode(values, plaintext); err != nil { panic(err) } @@ -157,7 +156,7 @@ func round(x float64) float64 { return math.Round(x*100000000) / 100000000 } -func printDebug(params ckks.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []float64, decryptor *rlwe.Decryptor, encoder *ckks.Encoder) (valuesTest []float64) { +func printDebug(params float.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []float64, decryptor *rlwe.Decryptor, encoder *float.Encoder) (valuesTest []float64) { valuesTest = make([]float64, 1< `rlwe` -> `ckks`. + // The `he/float` package relies on the `ckks` and `rlwe` packages, which themselves relies on the `ring` package: `ring` -> `rlwe` -> `ckks` -> `he/float`. // // The lowest layer is the `ring` package. // The `ring` package provides optimized arithmetic in rings `Z_{Q}[X]/(X^{N}+1)` for `N` a power of two and @@ -38,14 +37,16 @@ func main() { // but also more advanced operations such as the `Trace`. // // The top layer is the `ckks` package. - // This package implements the CKKS scheme, and mostly consist in defining the encoding and providing a user friendly API - // for the homomorphic operations. + // This package implements the CKKS scheme, and mostly consist in defining the encoding and scheme specific homomorphic operations. + // + // The user facing layer is the `he/float` package which implements high level functionalities and provides the a user with a + // friendly API for the homomorphic operations. // ======================================================= // `rlwe.Ciphertert`, `rlwe.Plaintext` and `rlwe.MetaData` // ======================================================= // - // Before talking about the capabilities of the `ckks` package, we have to give some information about the `rlwe.Ciphertext` and `rlwe.Plaintext` objects. + // Before talking about the capabilities of the `he/float` package, we have to give some information about the `rlwe.Ciphertext` and `rlwe.Plaintext` objects. // // Both contain the `rlwe.MetaData` struct, which notably holds the following fields: // - `Scale`: the scaling factor. This field is updated dynamically during computations. @@ -58,10 +59,10 @@ func main() { // These are all public fields which can be manually edited by advanced users if needed. // // ====================================================== - // Capabilities of the CKKS Scheme in the Lattigo Library + // Capabilities of the HE/FLOAT Package in the Lattigo Library // ====================================================== // - // The current capabilities of the `ckks` package are the following: + // The current capabilities of the `he/float` package are the following: // // - Encoding: encode vectors of type `[]complex128`, `[]float64`, `[]*big.Float` or `[]*bignum.Complex` on `rlwe.Plaintext` // @@ -98,28 +99,26 @@ func main() { // // - All methods of the `rlwe.Evaluator`, which are not described here. // - // The `ckks` package also contains two sub-packages: - // - `advanced`: homomorphic encoding/decoding (i.e. homomorphic switch between `SlotsDomain` and `CoefficientDomain`) and homomorphic modular reduction. - // - `bootstrapping`: bootstrapping for the CKKS scheme. + // The `he/float` package also contains the sub-packages: `bootstrapper` which implements bootstrapping to refresh ciphertexts, enabling arbitrary depth circuits. // - // Note that the package `ckks` also supports the real variant of the CKKS scheme, i.e. plaintext vector of R^{N} (instead of complex vectors C^{N/2}). + // Note that the package `he/float` also supports a real variant, i.e. plaintext vector of R^{N} (instead of complex vectors C^{N/2}). // A homomorphic bridge between the two schemes is also available. - // This variant can be activated by specifying the `ring.Type` to `ring.ConjugateInvariant` (i.e the ring Z[X + X^{-1}]/(X^{N}+1)) in the `ckks.Parameters` struct. + // This variant can be activated by specifying the `ring.Type` to `ring.ConjugateInvariant` (i.e the ring Z[X + X^{-1}]/(X^{N}+1)) in the `float.Parameters` struct. // ================================= - // Instantiating the ckks.Parameters + // Instantiating the float.Parameters // ================================= // - // We will instantiate a `ckks.Parameters` struct. + // We will instantiate a `float.Parameters` struct. // Unlike other libraries, `Lattigo` doesn't have, yet, a quick constructor. // Users must specify all parameters, up to each individual prime size. // // We will create parameters that are 128-bit secure and allow a depth 7 computation with a scaling factor of 2^{45}. var err error - var params ckks.Parameters - if params, err = ckks.NewParametersFromLiteral( - ckks.ParametersLiteral{ + var params float.Parameters + if params, err = float.NewParametersFromLiteral( + float.ParametersLiteral{ LogN: 14, // A ring degree of 2^{14} LogQ: []int{55, 45, 45, 45, 45, 45, 45, 45}, // An initial prime of 55 bits and 7 primes of 45 bits LogP: []int{61}, // The log2 size of the key-switching prime @@ -139,7 +138,7 @@ func main() { // This precision is notably the precision used by the encoder to encode/decode values. prec := params.EncodingPrecision() // we will need this value later - // Note that the following fields in the `ckks.ParametersLiteral`are optional, but can be manually specified by advanced users: + // Note that the following fields in the `float.ParametersLiteral`are optional, but can be manually specified by advanced users: // - `Xs`: the secret distribution (default uniform ternary) // - `Xe`: the error distribution (default discrete Gaussian with standard deviation of 3.2 and truncated to 19) // - `PowBase`: the log2 of the binary decomposition (default 0, i.e. infinity, i.e. no decomposition) @@ -153,7 +152,7 @@ func main() { // ============== // // To generate any key, be it the secret key, the public key or evaluation keys, we first need to instantiate the key generator. - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) // For now we will generate the following keys: // - SecretKey: the secret from which all other keys are derived @@ -192,17 +191,19 @@ func main() { // - `EncodingDomain`: `rlwe.SlotsDomain` (this is the default value) // - `LogSlots`: `params.MaxLogSlots` (which is LogN-1=13 in this example) // We can check that the plaintext was created at the maximum level with pt1.Level(). - pt1 := ckks.NewPlaintext(params, params.MaxLevel()) + pt1 := float.NewPlaintext(params, params.MaxLevel()) // Then we need to instantiate the encoder, which will enable us to embed our `values` of type `[]complex128` on a `rlwe.Plaintext`. // By default the encoder will use the params.DefaultPrecision(), but a user can specify a custom precision as an optional argument, - // for example `ckks.NewEncoder(params, 256)`. - ecd := ckks.NewEncoder(params) + // for example `float.NewEncoder(params, 256)`. + ecd := float.NewEncoder(params) + + ecd2 := float.NewEncoder(float.Parameters(params)) // And we encode our `values` on the plaintext. // Note that the encoder will check the metadata of the plaintext and adapt the encoding accordingly. // For example, one can modify the `Scale`, `EncodingDomain` or `LogSlots` fields change the way the encoding behaves. - if err = ecd.Encode(values1, pt1); err != nil { + if err = ecd2.Encode(values1, pt1); err != nil { panic(err) } @@ -213,7 +214,7 @@ func main() { // To generate ciphertexts we need an encryptor. // An encryptor will accept both a secret key or a public key, // in this example we will use the public key. - enc := ckks.NewEncryptor(params, pk) + enc := rlwe.NewEncryptor(params, pk) // And we create the ciphertext. // Note that the metadata of the plaintext will be copied on the resulting ciphertext. @@ -223,7 +224,7 @@ func main() { } // It is also possible to first allocate the ciphertext the same way it was done - // for the plaintext with with `ct := ckks.NewCiphertext(params, 1, pt.Level())`, + // for the plaintext with with `ct := float.NewCiphertext(params, 1, pt.Level())`, // enabling allocation free encryptions (for example if the ciphertext has to be // serialized right away). @@ -234,14 +235,14 @@ func main() { // We are able to generate ciphertext from plaintext using the encryptor. // To do the converse, generate plaintexts from ciphertexts, we need to instantiate a decryptor. // Obviously, the decryptor will only accept the secret key. - dec := ckks.NewDecryptor(params, sk) + dec := rlwe.NewDecryptor(params, sk) // ================ // Evaluator Basics // ================ // // Before anything, we must instantiate the evaluator, and we provide the evaluation key struct. - eval := ckks.NewEvaluator(params, evk) + eval := float.NewEvaluator(params, evk) // For the purpose of the example, we will create a second vector of random values. values2 := make([]complex128, Slots) @@ -249,7 +250,7 @@ func main() { values2[i] = complex(2*r.Float64()-1, 2*r.Float64()-1) } - pt2 := ckks.NewPlaintext(params, params.MaxLevel()) + pt2 := float.NewPlaintext(params, params.MaxLevel()) // =========================== // Managing the Scaling Factor @@ -257,7 +258,8 @@ func main() { // // Before going further and showcasing the capabilities of the evaluator, we must talk // about the maintenance of the scaling factor. - // This is a very central topic, especially for the full-RNS variant of the CKKS scheme. + // This is a very central topic, especially for the full-RNS variant of fixed-point + // approximate homomorphic encryption over the reals/complexes. // Messages are encoded on integer polynomials, and thus to keep the precision real // coefficients need to be scaled before being discretized to integers. // When two messages are multiplied together, the scaling factor of the resulting message @@ -285,7 +287,7 @@ func main() { fmt.Printf("========\n") fmt.Printf("\n") // Additions are often seen as a trivial operation. - // However in the case of the full-RNS variant of the CKKS scheme we have to be careful. + // However in the case of the full-RNS implementation we have to be careful. // Indeed, we must ensure that when adding two ciphertexts, those ciphertexts have the same exact scale, // else an error proportional to the difference of the scale will be introduced. // @@ -326,14 +328,14 @@ func main() { if err != nil { panic(err) } - fmt.Printf("Addition - ct + ct%s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Addition - ct + ct%s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) // ciphertext + plaintext ct3, err = eval.AddNew(ct1, pt2) if err != nil { panic(err) } - fmt.Printf("Addition - ct + pt%s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Addition - ct + pt%s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) // ciphertext + vector // Note that the evaluator will encode this vector at the scale of the input ciphertext to ensure a noiseless addition. @@ -341,7 +343,7 @@ func main() { if err != nil { panic(err) } - fmt.Printf("Addition - ct + vector%s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Addition - ct + vector%s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) // ciphertext + scalar scalar := 3.141592653589793 + 1.4142135623730951i @@ -354,7 +356,7 @@ func main() { if err != nil { panic(err) } - fmt.Printf("Addition - ct + scalar%s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Addition - ct + scalar%s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) fmt.Printf("==============\n") fmt.Printf("MULTIPLICATION\n") @@ -418,14 +420,14 @@ func main() { // For the sake of conciseness, we will not rescale the output for the other multiplication example. // But this maintenance operation should usually be called (either before of after the multiplication depending on the choice of noise management) // to control the magnitude of the plaintext scale. - fmt.Printf("Multiplication - ct * ct%s", ckks.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) + fmt.Printf("Multiplication - ct * ct%s", float.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) // ciphertext + plaintext ct3, err = eval.MulRelinNew(ct1, pt2) if err != nil { panic(err) } - fmt.Printf("Multiplication - ct * pt%s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Multiplication - ct * pt%s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) // ciphertext + vector // Note that when giving non-encoded vectors, the evaluator will internally encode this vector with the appropriate scale that ensure that @@ -434,7 +436,7 @@ func main() { if err != nil { panic(err) } - fmt.Printf("Multiplication - ct * vector%s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Multiplication - ct * vector%s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) // ciphertext + scalar (scalar = pi + sqrt(2) * i) for i := 0; i < Slots; i++ { @@ -448,7 +450,7 @@ func main() { if err != nil { panic(err) } - fmt.Printf("Multiplication - ct * scalar%s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Multiplication - ct * scalar%s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) fmt.Printf("======================\n") fmt.Printf("ROTATION & CONJUGATION\n") @@ -466,12 +468,9 @@ func main() { // This corresponds to the following values for k which we call "galois elements": rot := 5 galEls := []uint64{ - //the galois element for the cyclic rotations by 5 positions to the left. + // The galois element for the cyclic rotations by 5 positions to the left. params.GaloisElement(rot), - // the galois element for the complex conjugate (The CKKS scheme actually encrypts 2xN/2 values, so the conjugate operation can be seen - // as a rotation between the row which contains the real part and that which contains the complex part of the complex values). - // The reason for this name is that the `ckks` package does not yet have a wrapper for this method which comes from the `rlwe` package. - // The name of this method comes from the BFV/BGV schemes, which have plaintext spaces of Z_{2xN/2}, i.e. a matrix of 2 rows and N/2 columns. + // The galois element for the complex conjugatation. params.GaloisElementForComplexConjugation(), } @@ -488,7 +487,7 @@ func main() { if err != nil { panic(err) } - fmt.Printf("Rotation by k=%d %s", rot, ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Rotation by k=%d %s", rot, float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) // Conjugation for i := 0; i < Slots; i++ { @@ -499,7 +498,7 @@ func main() { if err != nil { panic(err) } - fmt.Printf("Conjugation %s", ckks.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) + fmt.Printf("Conjugation %s", float.GetPrecisionStats(params, ecd, dec, want, ct3, 0, false).String()) // Note that rotations and conjugation only add a fixed additive noise independent of the ciphertext noise. // If the parameters are set correctly, this noise can be rounding error (thus negligible). @@ -574,20 +573,20 @@ func main() { panic(err) } - fmt.Printf("Polynomial Evaluation %s", ckks.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) + fmt.Printf("Polynomial Evaluation %s", float.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) // ============================= // Vector Polynomials Evaluation // ============================= // - // See `examples/ckks/polyeval` + // See `examples/hefloat/polyeval` fmt.Printf("======================\n") fmt.Printf("LINEAR TRANSFORMATIONS\n") fmt.Printf("======================\n") fmt.Printf("\n") - // The `ckks` package provides a multiple handy linear transformations. + // The `he/float` package provides a multiple handy linear transformations. // We will start with the inner sum. // Thus method allows to aggregate `n` sub-vectors of size `batch`. // For example given a vector [x0, x1, x2, x3, x4, x5, x6, x7], batch = 2 and n = 3 @@ -616,7 +615,7 @@ func main() { // Note that this method can obviously be used to average values. // For a good noise management, it is recommended to first multiply the values by 1/n, then // apply the innersum and then only apply the rescaling. - fmt.Printf("Innersum %s", ckks.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) + fmt.Printf("Innersum %s", float.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) // The replicate operation is exactly the same as the innersum operation, but in reverse eval = eval.WithKey(rlwe.NewMemEvaluationKeySet(rlk, kgen.GenGaloisKeysNew(params.GaloisElementsForReplicate(batch, n), sk)...)) @@ -633,7 +632,7 @@ func main() { panic(err) } - fmt.Printf("Replicate %s", ckks.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) + fmt.Printf("Replicate %s", float.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) // And we arrive to the linear transformation. // This method enables to evaluate arbitrary Slots x Slots matrices on a ciphertext. @@ -713,19 +712,19 @@ func main() { // We evaluate the same circuit in plaintext want = EvaluateLinearTransform(values1, diagonals) - fmt.Printf("vector x matrix %s", ckks.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) + fmt.Printf("vector x matrix %s", float.GetPrecisionStats(params, ecd, dec, want, res, 0, false).String()) // ============================= // Homomorphic Encoding/Decoding // ============================= // - // See `examples/ckks/advanced/lut` + // See `examples/hefloat/advanced/lut` // ============ // Bootstrapping // ============ // - // See `examples/ckks/bootstrapping` + // See `examples/hefloat/bootstrapping` // ========== // CONCURRENCY diff --git a/examples/ckks/template/main_test.go b/examples/hefloat/tutorial/main_test.go similarity index 100% rename from examples/ckks/template/main_test.go rename to examples/hefloat/tutorial/main_test.go diff --git a/he/blindrotation/evaluator.go b/he/blindrotation/evaluator.go index 3d8e1b52..56172c26 100644 --- a/he/blindrotation/evaluator.go +++ b/he/blindrotation/evaluator.go @@ -26,19 +26,19 @@ type Evaluator struct { } // NewEvaluator instantiates a new Evaluator. -func NewEvaluator(paramsBR, paramsLWE rlwe.Parameters) (eval *Evaluator) { +func NewEvaluator(paramsBR, paramsLWE rlwe.ParameterProvider) (eval *Evaluator) { eval = new(Evaluator) eval.Evaluator = rgsw.NewEvaluator(paramsBR, nil) - eval.paramsBR = paramsBR - eval.paramsLWE = paramsLWE + eval.paramsBR = *paramsBR.GetRLWEParameters() + eval.paramsLWE = *paramsLWE.GetRLWEParameters() - eval.poolMod2N = [2]ring.Poly{paramsLWE.RingQ().NewPoly(), paramsLWE.RingQ().NewPoly()} - eval.accumulator = rlwe.NewCiphertext(paramsBR, 1, paramsBR.MaxLevel()) + eval.poolMod2N = [2]ring.Poly{eval.paramsLWE.RingQ().NewPoly(), eval.paramsLWE.RingQ().NewPoly()} + eval.accumulator = rlwe.NewCiphertext(paramsBR, 1, eval.paramsBR.MaxLevel()) eval.accumulator.IsNTT = true // This flag is always true // Generates a map for the discrete log of (+/- 1) * GaloisGen^k for 0 <= k < N-1. // galoisGenDiscreteLog: map[+/-G^{k} mod 2N] = k - eval.galoisGenDiscreteLog = getGaloisElementInverseMap(ring.GaloisGen, paramsBR.N()) + eval.galoisGenDiscreteLog = getGaloisElementInverseMap(ring.GaloisGen, eval.paramsBR.N()) return } diff --git a/he/blindrotation/keys.go b/he/blindrotation/keys.go index 8db4435b..93d44713 100644 --- a/he/blindrotation/keys.go +++ b/he/blindrotation/keys.go @@ -43,22 +43,25 @@ func (evk MemBlindRotationEvaluationKeySet) GetEvaluationKeySet() (rlwe.Evaluati } // GenEvaluationKeyNew generates a new Blind Rotation evaluation key -func GenEvaluationKeyNew(paramsRLWE rlwe.Parameters, skRLWE *rlwe.SecretKey, paramsLWE rlwe.Parameters, skLWE *rlwe.SecretKey, evkParams ...rlwe.EvaluationKeyParameters) (key MemBlindRotationEvaluationKeySet) { +func GenEvaluationKeyNew(paramsRLWE rlwe.ParameterProvider, skRLWE *rlwe.SecretKey, paramsLWE rlwe.ParameterProvider, skLWE *rlwe.SecretKey, evkParams ...rlwe.EvaluationKeyParameters) (key MemBlindRotationEvaluationKeySet) { + + pRLWE := *paramsRLWE.GetRLWEParameters() + pLWE := *paramsLWE.GetRLWEParameters() skLWECopy := skLWE.CopyNew() - paramsLWE.RingQ().AtLevel(0).INTT(skLWECopy.Value.Q, skLWECopy.Value.Q) - paramsLWE.RingQ().AtLevel(0).IMForm(skLWECopy.Value.Q, skLWECopy.Value.Q) - sk := make([]*big.Int, paramsLWE.N()) + pLWE.RingQ().AtLevel(0).INTT(skLWECopy.Value.Q, skLWECopy.Value.Q) + pLWE.RingQ().AtLevel(0).IMForm(skLWECopy.Value.Q, skLWECopy.Value.Q) + sk := make([]*big.Int, pLWE.N()) for i := range sk { sk[i] = new(big.Int) } - paramsLWE.RingQ().AtLevel(0).PolyToBigintCentered(skLWECopy.Value.Q, 1, sk) + pLWE.RingQ().AtLevel(0).PolyToBigintCentered(skLWECopy.Value.Q, 1, sk) - encryptor := rgsw.NewEncryptor(paramsRLWE, skRLWE) + encryptor := rgsw.NewEncryptor(pRLWE, skRLWE) - levelQ, levelP, BaseTwoDecomposition := rlwe.ResolveEvaluationKeyParameters(paramsRLWE, evkParams) + levelQ, levelP, BaseTwoDecomposition := rlwe.ResolveEvaluationKeyParameters(pRLWE, evkParams) - skiRGSW := make([]*rgsw.Ciphertext, paramsLWE.N()) + skiRGSW := make([]*rgsw.Ciphertext, pLWE.N()) ptXi := make(map[int]*rlwe.Plaintext) @@ -71,13 +74,13 @@ func GenEvaluationKeyNew(paramsRLWE rlwe.Parameters, skRLWE *rlwe.SecretKey, par pt := &rlwe.Plaintext{} pt.MetaData = &rlwe.MetaData{} pt.IsNTT = true - pt.Value = paramsRLWE.RingQ().NewMonomialXi(siInt) - paramsRLWE.RingQ().NTT(pt.Value, pt.Value) + pt.Value = pRLWE.RingQ().NewMonomialXi(siInt) + pRLWE.RingQ().NTT(pt.Value, pt.Value) ptXi[siInt] = pt } - skiRGSW[i] = rgsw.NewCiphertext(paramsRLWE, levelQ, levelP, BaseTwoDecomposition) + skiRGSW[i] = rgsw.NewCiphertext(pRLWE, levelQ, levelP, BaseTwoDecomposition) // Sanity check, this error should never happen unless this algorithm // has been improperly modified to provides invalid inputs. @@ -86,14 +89,14 @@ func GenEvaluationKeyNew(paramsRLWE rlwe.Parameters, skRLWE *rlwe.SecretKey, par } } - kgen := rlwe.NewKeyGenerator(paramsRLWE) + kgen := rlwe.NewKeyGenerator(pRLWE) galEls := make([]uint64, windowSize) for i := 0; i < windowSize; i++ { - galEls[i] = paramsRLWE.GaloisElement(i + 1) + galEls[i] = pRLWE.GaloisElement(i + 1) } - galEls = append(galEls, paramsRLWE.RingQ().NthRoot()-ring.GaloisGen) + galEls = append(galEls, pRLWE.RingQ().NthRoot()-ring.GaloisGen) gks := kgen.GenGaloisKeysNew(galEls, skRLWE, rlwe.EvaluationKeyParameters{ LevelQ: utils.Pointy(levelQ), diff --git a/he/float/bootstrapper/bootstrapper.go b/he/float/bootstrapper/bootstrapper.go index fb8b9d01..012b608c 100644 --- a/he/float/bootstrapper/bootstrapper.go +++ b/he/float/bootstrapper/bootstrapper.go @@ -51,7 +51,7 @@ func NewBootstrapper(btpParams Parameters, evk *BootstrappingKeys) (*Bootstrappe } var err error - if b.bridge, err = ckks.NewDomainSwitcher(paramsN2, evk.EvkCmplxToReal, evk.EvkRealToCmplx); err != nil { + if b.bridge, err = ckks.NewDomainSwitcher(paramsN2.Parameters, evk.EvkCmplxToReal, evk.EvkRealToCmplx); err != nil { return nil, fmt.Errorf("cannot NewBootstrapper: ckks.NewDomainSwitcher: %w", err) } @@ -209,7 +209,7 @@ func (b Bootstrapper) refreshConjugateInvariant(ctLeftN1Q0, ctRightN1Q0 *rlwe.Ci // Extracts the imaginary part if ctRightN1Q0 != nil { - if err = b.bootstrapper.Mul(ctLeftAndRightN2QL, -1i, ctLeftAndRightN2QL); err != nil { + if err = b.bootstrapper.Evaluator.Mul(ctLeftAndRightN2QL, -1i, ctLeftAndRightN2QL); err != nil { return nil, nil, fmt.Errorf("cannot BootstrapMany: %w", err) } ctRightN1QL = b.ComplexToRealNew(ctLeftAndRightN2QL) diff --git a/he/float/bootstrapper/bootstrapper_test.go b/he/float/bootstrapper/bootstrapper_test.go index 36887256..a4382f49 100644 --- a/he/float/bootstrapper/bootstrapper_test.go +++ b/he/float/bootstrapper/bootstrapper_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils" @@ -17,7 +17,7 @@ import ( var flagLongTest = flag.Bool("long", false, "run the long test suite (all parameters + secure bootstrapping). Overrides -short and requires -timeout=0.") var printPrecisionStats = flag.Bool("print-precision", false, "print precision stats") -var testPrec45 = ckks.ParametersLiteral{ +var testPrec45 = float.ParametersLiteral{ LogN: 10, LogQ: []int{60, 40}, LogP: []int{61}, @@ -38,7 +38,7 @@ func TestBootstrapping(t *testing.T) { schemeParamsLit.LogN = 16 } - params, err := ckks.NewParametersFromLiteral(schemeParamsLit) + params, err := float.NewParametersFromLiteral(schemeParamsLit) require.Nil(t, err) btpParamsLit.LogN = utils.Pointy(params.LogN()) @@ -57,7 +57,7 @@ func TestBootstrapping(t *testing.T) { t.Logf("ParamsN2: LogN=%d/LogSlots=%d/LogQP=%f", params.LogN(), params.LogMaxSlots(), params.LogQP()) - sk := ckks.NewKeyGenerator(btpParams.Parameters.Parameters).GenSecretKeyNew() + sk := rlwe.NewKeyGenerator(btpParams.Parameters.Parameters).GenSecretKeyNew() t.Log("Generating Bootstrapping Keys") btpKeys, _, err := btpParams.GenBootstrappingKeys(sk) @@ -66,9 +66,9 @@ func TestBootstrapping(t *testing.T) { bootstrapper, err := NewBootstrapper(btpParams, btpKeys) require.NoError(t, err) - ecd := ckks.NewEncoder(params) - enc := ckks.NewEncryptor(params, sk) - dec := ckks.NewDecryptor(params, sk) + ecd := float.NewEncoder(params) + enc := rlwe.NewEncryptor(params, sk) + dec := rlwe.NewDecryptor(params, sk) values := make([]complex128, params.MaxSlots()) for i := range values { @@ -84,7 +84,7 @@ func TestBootstrapping(t *testing.T) { t.Run("Bootstrapping", func(t *testing.T) { - plaintext := ckks.NewPlaintext(params, 0) + plaintext := float.NewPlaintext(params, 0) ecd.Encode(values, plaintext) ctQ0, err := enc.EncryptNew(plaintext) @@ -117,7 +117,7 @@ func TestBootstrapping(t *testing.T) { schemeParamsLit.LogNthRoot = schemeParamsLit.LogN + 1 schemeParamsLit.LogN-- - params, err := ckks.NewParametersFromLiteral(schemeParamsLit) + params, err := float.NewParametersFromLiteral(schemeParamsLit) require.Nil(t, err) btpParamsLit.LogN = utils.Pointy(params.LogN() + 1) @@ -137,7 +137,7 @@ func TestBootstrapping(t *testing.T) { t.Logf("Params: LogN=%d/LogSlots=%d/LogQP=%f", params.LogN(), params.LogMaxSlots(), params.LogQP()) t.Logf("BTPParams: LogN=%d/LogSlots=%d/LogQP=%f", btpParams.LogN(), btpParams.LogMaxSlots(), btpParams.LogQP()) - sk := ckks.NewKeyGenerator(params).GenSecretKeyNew() + sk := rlwe.NewKeyGenerator(params).GenSecretKeyNew() t.Log("Generating Bootstrapping Keys") btpKeys, _, err := btpParams.GenBootstrappingKeys(sk) @@ -146,9 +146,9 @@ func TestBootstrapping(t *testing.T) { bootstrapper, err := NewBootstrapper(btpParams, btpKeys) require.Nil(t, err) - ecd := ckks.NewEncoder(params) - enc := ckks.NewEncryptor(params, sk) - dec := ckks.NewDecryptor(params, sk) + ecd := float.NewEncoder(params) + enc := rlwe.NewEncryptor(params, sk) + dec := rlwe.NewDecryptor(params, sk) values := make([]complex128, params.MaxSlots()) for i := range values { @@ -164,7 +164,7 @@ func TestBootstrapping(t *testing.T) { t.Run("N1ToN2->Bootstrapping->N2ToN1", func(t *testing.T) { - plaintext := ckks.NewPlaintext(params, 0) + plaintext := float.NewPlaintext(params, 0) ecd.Encode(values, plaintext) ctQ0, err := enc.EncryptNew(plaintext) @@ -202,7 +202,7 @@ func TestBootstrapping(t *testing.T) { schemeParamsLit.LogNthRoot = schemeParamsLit.LogN + 1 schemeParamsLit.LogN -= 3 - params, err := ckks.NewParametersFromLiteral(schemeParamsLit) + params, err := float.NewParametersFromLiteral(schemeParamsLit) require.Nil(t, err) btpParams, err := NewParametersFromLiteral(params, btpParamsLit) @@ -220,7 +220,7 @@ func TestBootstrapping(t *testing.T) { t.Logf("Params: LogN=%d/LogSlots=%d/LogQP=%f", params.LogN(), params.LogMaxSlots(), params.LogQP()) t.Logf("BTPParams: LogN=%d/LogSlots=%d/LogQP=%f", btpParams.LogN(), btpParams.LogMaxSlots(), btpParams.LogQP()) - sk := ckks.NewKeyGenerator(params).GenSecretKeyNew() + sk := rlwe.NewKeyGenerator(params).GenSecretKeyNew() t.Log("Generating Bootstrapping Keys") btpKeys, _, err := btpParams.GenBootstrappingKeys(sk) @@ -229,9 +229,9 @@ func TestBootstrapping(t *testing.T) { bootstrapper, err := NewBootstrapper(btpParams, btpKeys) require.Nil(t, err) - ecd := ckks.NewEncoder(params) - enc := ckks.NewEncryptor(params, sk) - dec := ckks.NewDecryptor(params, sk) + ecd := float.NewEncoder(params) + enc := rlwe.NewEncryptor(params, sk) + dec := rlwe.NewDecryptor(params, sk) values := make([]complex128, params.MaxSlots()) for i := range values { @@ -245,7 +245,7 @@ func TestBootstrapping(t *testing.T) { values[3] = complex(0.9238795325112867, 0.3826834323650898) } - pt := ckks.NewPlaintext(params, 0) + pt := float.NewPlaintext(params, 0) cts := make([]rlwe.Ciphertext, 7) for i := range cts { @@ -285,7 +285,7 @@ func TestBootstrapping(t *testing.T) { schemeParamsLit.LogNthRoot = schemeParamsLit.LogN + 1 schemeParamsLit.LogN-- - params, err := ckks.NewParametersFromLiteral(schemeParamsLit) + params, err := float.NewParametersFromLiteral(schemeParamsLit) require.Nil(t, err) btpParams, err := NewParametersFromLiteral(params, btpParamsLit) @@ -300,7 +300,7 @@ func TestBootstrapping(t *testing.T) { t.Logf("Params: LogN=%d/LogSlots=%d/LogQP=%f", params.LogN(), params.LogMaxSlots(), params.LogQP()) t.Logf("BTPParams: LogN=%d/LogSlots=%d/LogQP=%f", btpParams.LogN(), btpParams.LogMaxSlots(), btpParams.LogQP()) - sk := ckks.NewKeyGenerator(params).GenSecretKeyNew() + sk := rlwe.NewKeyGenerator(params).GenSecretKeyNew() t.Log("Generating Bootstrapping Keys") btpKeys, _, err := btpParams.GenBootstrappingKeys(sk) @@ -309,9 +309,9 @@ func TestBootstrapping(t *testing.T) { bootstrapper, err := NewBootstrapper(btpParams, btpKeys) require.Nil(t, err) - ecd := ckks.NewEncoder(params) - enc := ckks.NewEncryptor(params, sk) - dec := ckks.NewDecryptor(params, sk) + ecd := float.NewEncoder(params) + enc := rlwe.NewEncryptor(params, sk) + dec := rlwe.NewDecryptor(params, sk) values := make([]float64, params.MaxSlots()) for i := range values { @@ -327,7 +327,7 @@ func TestBootstrapping(t *testing.T) { t.Run("ConjugateInvariant->Standard->Bootstrapping->Standard->ConjugateInvariant", func(t *testing.T) { - plaintext := ckks.NewPlaintext(params, 0) + plaintext := float.NewPlaintext(params, 0) require.NoError(t, ecd.Encode(values, plaintext)) ctLeftQ0, err := enc.EncryptNew(plaintext) @@ -357,8 +357,8 @@ func TestBootstrapping(t *testing.T) { }) } -func verifyTestVectorsBootstrapping(params ckks.Parameters, encoder *ckks.Encoder, decryptor *rlwe.Decryptor, valuesWant, element interface{}, t *testing.T) { - precStats := ckks.GetPrecisionStats(params, encoder, decryptor, valuesWant, element, 0, false) +func verifyTestVectorsBootstrapping(params float.Parameters, encoder *float.Encoder, decryptor *rlwe.Decryptor, valuesWant, element interface{}, t *testing.T) { + precStats := float.GetPrecisionStats(params, encoder, decryptor, valuesWant, element, 0, false) if *printPrecisionStats { t.Log(precStats.String()) } diff --git a/he/float/bootstrapper/bootstrapping/bootstrapper.go b/he/float/bootstrapper/bootstrapping/bootstrapper.go index e55b5d63..5eb78d74 100644 --- a/he/float/bootstrapper/bootstrapping/bootstrapper.go +++ b/he/float/bootstrapper/bootstrapping/bootstrapper.go @@ -5,7 +5,6 @@ import ( "math" "math/big" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/rlwe" ) @@ -13,7 +12,7 @@ import ( // Bootstrapper is a struct to store a memory buffer with the plaintext matrices, // the polynomial approximation, and the keys for the bootstrapping. type Bootstrapper struct { - *ckks.Evaluator + *float.Evaluator *float.DFTEvaluator *float.Mod1Evaluator *bootstrapperBase @@ -23,7 +22,7 @@ type Bootstrapper struct { type bootstrapperBase struct { Parameters *EvaluationKeySet - params ckks.Parameters + params float.Parameters dslots int // Number of plaintext slots after the re-encoding logdslots int @@ -35,7 +34,7 @@ type bootstrapperBase struct { q0OverMessageRatio float64 } -// EvaluationKeySet is a type for a CKKS bootstrapping key, which +// EvaluationKeySet is a type for a bootstrapping key, which // regroups the necessary public relinearization and rotation keys. type EvaluationKeySet struct { *rlwe.MemEvaluationKeySet @@ -51,7 +50,7 @@ func NewBootstrapper(btpParams Parameters, btpKeys *EvaluationKeySet) (btp *Boot } if btpParams.Mod1ParametersLiteral.Mod1Type == float.CosDiscrete && btpParams.Mod1ParametersLiteral.Mod1Degree < 2*(btpParams.Mod1ParametersLiteral.K-1) { - return nil, fmt.Errorf("Mod1Type 'ckks.CosDiscrete' uses a minimum degree of 2*(K-1) but EvalMod degree is smaller") + return nil, fmt.Errorf("Mod1Type 'float.CosDiscrete' uses a minimum degree of 2*(K-1) but EvalMod degree is smaller") } if btpParams.CoeffsToSlotsParameters.LevelStart-btpParams.CoeffsToSlotsParameters.Depth(true) != btpParams.Mod1ParametersLiteral.LevelStart { @@ -75,7 +74,7 @@ func NewBootstrapper(btpParams Parameters, btpKeys *EvaluationKeySet) (btp *Boot btp.EvaluationKeySet = btpKeys - btp.Evaluator = ckks.NewEvaluator(params, btpKeys) + btp.Evaluator = float.NewEvaluator(params, btpKeys) btp.DFTEvaluator = float.NewDFTEvaluator(params, btp.Evaluator) @@ -124,7 +123,7 @@ func (p Parameters) GenEvaluationKeySetNew(sk *rlwe.SecretKey) *EvaluationKeySet // Extends basis Q0 -> P rlwe.ExtendBasisSmallNormAndCenterNTTMontgomery(ringQ, ringP, sk.Value.Q, buff, skExtended.Value.P) - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) EvkDtS, EvkStD := p.GenEncapsulationEvaluationKeysNew(skExtended) @@ -187,7 +186,7 @@ func (bb *bootstrapperBase) CheckKeys(btpKeys *EvaluationKeySet) (err error) { return } -func newBootstrapperBase(params ckks.Parameters, btpParams Parameters, btpKey *EvaluationKeySet) (bb *bootstrapperBase, err error) { +func newBootstrapperBase(params float.Parameters, btpParams Parameters, btpKey *EvaluationKeySet) (bb *bootstrapperBase, err error) { bb = new(bootstrapperBase) bb.params = params bb.Parameters = btpParams @@ -225,7 +224,7 @@ func newBootstrapperBase(params ckks.Parameters, btpParams Parameters, btpKey *E qDiv = 1 } - encoder := ckks.NewEncoder(bb.params) + encoder := float.NewEncoder(bb.params) // CoeffsToSlots vectors // Change of variable for the evaluation of the Chebyshev polynomial + cancelling factor for the DFT and SubSum + eventual scaling factor for the double angle formula diff --git a/he/float/bootstrapper/bootstrapping/bootstrapping.go b/he/float/bootstrapper/bootstrapping/bootstrapping.go index 29035dd3..bc37b95a 100644 --- a/he/float/bootstrapper/bootstrapping/bootstrapping.go +++ b/he/float/bootstrapper/bootstrapping/bootstrapping.go @@ -1,4 +1,4 @@ -// Package bootstrapping implement the bootstrapping for the CKKS scheme. +// Package bootstrapping implement the bootstrapping for fixed-point fixed-point approximate arithmetic over the reals/complexes package bootstrapping import ( @@ -50,7 +50,7 @@ func (btp Bootstrapper) Bootstrap(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext diffScale := ctIn.Scale.Div(ctOut.Scale).Bigint() // [M^{d} + e^{d-logprec}] - if err = btp.Mul(ctOut, diffScale, ctOut); err != nil { + if err = btp.Evaluator.Mul(ctOut, diffScale, ctOut); err != nil { return nil, err } ctOut.Scale = ctIn.Scale @@ -83,14 +83,14 @@ func (btp Bootstrapper) Bootstrap(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext } // [M^{d} + e^{d-logprec}] - [M^{d}] -> [e^{d-logprec}] - tmp, err := btp.SubNew(ctOut, ctIn) + tmp, err := btp.Evaluator.SubNew(ctOut, ctIn) if err != nil { return nil, err } // prec * [e^{d-logprec}] -> [e^{d}] - if err = btp.Mul(tmp, prec, tmp); err != nil { + if err = btp.Evaluator.Mul(tmp, prec, tmp); err != nil { return nil, err } @@ -111,7 +111,7 @@ func (btp Bootstrapper) Bootstrap(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext // [[e^{d}/q1 + e'^{d-logprec}] * q1/logprec -> [e^{d-logprec} + e'^{d-2logprec}*q1] // If scale > 2^{logprec}, then we ensure a precision of at least 2^{logprec} even with a rounding of the scale if !requiresReservedPrime { - if err = btp.Mul(tmp, scale, tmp); err != nil { + if err = btp.Evaluator.Mul(tmp, scale, tmp); err != nil { return nil, err } } else { @@ -121,12 +121,12 @@ func (btp Bootstrapper) Bootstrap(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext ss.Quo(ss, new(big.Float).SetInt(prec)) // Do a scaled multiplication by the last prime - if err = btp.Mul(tmp, ss, tmp); err != nil { + if err = btp.Evaluator.Mul(tmp, ss, tmp); err != nil { return nil, err } // And rescale - if err = btp.Rescale(tmp, tmp); err != nil { + if err = btp.Evaluator.Rescale(tmp, tmp); err != nil { return nil, err } } @@ -135,7 +135,7 @@ func (btp Bootstrapper) Bootstrap(ctIn *rlwe.Ciphertext) (ctOut *rlwe.Ciphertext tmp.Scale = ctOut.Scale // [M^{d} + e^{d-logprec}] - [e^{d-logprec} + e'^{d-2logprec}*q1] -> [M^{d} + e'^{d-2logprec}*q1] - if err = btp.Sub(ctOut, tmp, ctOut); err != nil { + if err = btp.Evaluator.Sub(ctOut, tmp, ctOut); err != nil { return nil, err } } @@ -180,7 +180,7 @@ func (btp Bootstrapper) scaleDownToQ0OverMessageRatio(ctIn *rlwe.Ciphertext) (*r scaleUpBigint := scaleUp.Bigint() - if err := btp.Mul(ctIn, scaleUpBigint, ctIn); err != nil { + if err := btp.Evaluator.Mul(ctIn, scaleUpBigint, ctIn); err != nil { return nil, nil, fmt.Errorf("cannot scaleDownToQ0OverMessageRatio: %w", err) } diff --git a/he/float/bootstrapper/bootstrapping/bootstrapping_bench_test.go b/he/float/bootstrapper/bootstrapping/bootstrapping_bench_test.go index 8b7a3491..c9da5fc0 100644 --- a/he/float/bootstrapper/bootstrapping/bootstrapping_bench_test.go +++ b/he/float/bootstrapper/bootstrapping/bootstrapping_bench_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/stretchr/testify/require" - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/rlwe" ) @@ -14,13 +14,13 @@ func BenchmarkBootstrap(b *testing.B) { paramSet := DefaultParametersDense[0] - params, err := ckks.NewParametersFromLiteral(paramSet.SchemeParams) + params, err := float.NewParametersFromLiteral(paramSet.SchemeParams) require.NoError(b, err) btpParams, err := NewParametersFromLiteral(params, paramSet.BootstrappingParams) require.Nil(b, err) - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) sk := kgen.GenSecretKeyNew() btp, err := NewBootstrapper(btpParams, btpParams.GenEvaluationKeySetNew(sk)) @@ -35,7 +35,7 @@ func BenchmarkBootstrap(b *testing.B) { 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) + ct := float.NewCiphertext(params, 1, 0) ct.Scale = bootstrappingScale b.StartTimer() diff --git a/he/float/bootstrapper/bootstrapping/bootstrapping_test.go b/he/float/bootstrapper/bootstrapping/bootstrapping_test.go index 4c8c568a..a1155c70 100644 --- a/he/float/bootstrapper/bootstrapping/bootstrapping_test.go +++ b/he/float/bootstrapper/bootstrapping/bootstrapping_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils" "github.com/tuneinsight/lattigo/v4/utils/sampling" @@ -19,7 +19,7 @@ var minPrec float64 = 12.0 var flagLongTest = flag.Bool("long", false, "run the long test suite (all parameters + secure bootstrapping). Overrides -short and requires -timeout=0.") var printPrecisionStats = flag.Bool("print-precision", false, "print precision stats") -func ParamsToString(params ckks.Parameters, LogSlots int, opname string) string { +func ParamsToString(params float.Parameters, LogSlots int, opname string) string { return fmt.Sprintf("%slogN=%d/LogSlots=%d/logQP=%f/levels=%d/a=%d/b=%d", opname, params.LogN(), @@ -57,7 +57,7 @@ func TestBootstrapParametersMarshalling(t *testing.T) { t.Run("Parameters", func(t *testing.T) { paramSet := DefaultParametersSparse[0] - params, err := ckks.NewParametersFromLiteral(paramSet.SchemeParams) + params, err := float.NewParametersFromLiteral(paramSet.SchemeParams) require.Nil(t, err) btpParams, err := NewParametersFromLiteral(params, paramSet.BootstrappingParams) @@ -97,7 +97,7 @@ func TestBootstrappingWithEncapsulation(t *testing.T) { paramsSetCpy.BootstrappingParams.LogSlots = &LogSlots - params, err := ckks.NewParametersFromLiteral(paramsSetCpy.SchemeParams) + params, err := float.NewParametersFromLiteral(paramsSetCpy.SchemeParams) require.NoError(t, err) btpParams, err := NewParametersFromLiteral(params, paramsSetCpy.BootstrappingParams) @@ -140,7 +140,7 @@ func TestBootstrappingOriginal(t *testing.T) { paramsSetCpy.BootstrappingParams.LogSlots = &LogSlots - params, err := ckks.NewParametersFromLiteral(paramsSetCpy.SchemeParams) + params, err := float.NewParametersFromLiteral(paramsSetCpy.SchemeParams) require.NoError(t, err) btpParams, err := NewParametersFromLiteral(params, paramsSetCpy.BootstrappingParams) @@ -159,16 +159,16 @@ func TestBootstrappingOriginal(t *testing.T) { testBootstrapHighPrecision(paramSet, t) } -func testbootstrap(params ckks.Parameters, btpParams Parameters, level int, t *testing.T) { +func testbootstrap(params float.Parameters, btpParams Parameters, level int, t *testing.T) { t.Run(ParamsToString(params, btpParams.LogMaxSlots(), ""), func(t *testing.T) { - kgen := ckks.NewKeyGenerator(btpParams.Parameters) + kgen := rlwe.NewKeyGenerator(btpParams.Parameters) sk := kgen.GenSecretKeyNew() - encoder := ckks.NewEncoder(params) + encoder := float.NewEncoder(params) - encryptor := ckks.NewEncryptor(params, sk) - decryptor := ckks.NewDecryptor(params, sk) + encryptor := rlwe.NewEncryptor(params, sk) + decryptor := rlwe.NewDecryptor(params, sk) evk := btpParams.GenEvaluationKeySetNew(sk) @@ -188,7 +188,7 @@ func testbootstrap(params ckks.Parameters, btpParams Parameters, level int, t *t values[3] = complex(0.9238795325112867, 0.3826834323650898) } - plaintext := ckks.NewPlaintext(params, 0) + plaintext := float.NewPlaintext(params, 0) plaintext.Scale = params.DefaultScale() plaintext.LogDimensions = btpParams.LogMaxDimensions() encoder.Encode(values, plaintext) @@ -241,7 +241,7 @@ func testBootstrapHighPrecision(paramSet defaultParametersLiteral, t *testing.T) ReservedPrimeBitSize: 28, } - params, err := ckks.NewParametersFromLiteral(paramSet.SchemeParams) + params, err := float.NewParametersFromLiteral(paramSet.SchemeParams) if err != nil { panic(err) } @@ -260,11 +260,11 @@ func testBootstrapHighPrecision(paramSet defaultParametersLiteral, t *testing.T) t.Run(ParamsToString(params, btpParams.LogMaxSlots(), ""), func(t *testing.T) { - kgen := ckks.NewKeyGenerator(btpParams.Parameters) + kgen := rlwe.NewKeyGenerator(btpParams.Parameters) sk := kgen.GenSecretKeyNew() - encoder := ckks.NewEncoder(params, 164) - encryptor := ckks.NewEncryptor(params, sk) - decryptor := ckks.NewDecryptor(params, sk) + encoder := float.NewEncoder(params, 164) + encryptor := rlwe.NewEncryptor(params, sk) + decryptor := rlwe.NewDecryptor(params, sk) evk := btpParams.GenEvaluationKeySetNew(sk) @@ -284,7 +284,7 @@ func testBootstrapHighPrecision(paramSet defaultParametersLiteral, t *testing.T) values[3] = complex(0.9238795325112867, 0.3826834323650898) } - plaintext := ckks.NewPlaintext(params, level-1) + plaintext := float.NewPlaintext(params, level-1) plaintext.Scale = params.DefaultScale() for i := 0; i < plaintext.Level(); i++ { plaintext.Scale = plaintext.Scale.Mul(rlwe.NewScale(1 << 40)) @@ -308,8 +308,8 @@ func testBootstrapHighPrecision(paramSet defaultParametersLiteral, t *testing.T) }) } -func verifyTestVectors(params ckks.Parameters, encoder *ckks.Encoder, decryptor *rlwe.Decryptor, valuesWant, valuesHave interface{}, t *testing.T) { - precStats := ckks.GetPrecisionStats(params, encoder, decryptor, valuesWant, valuesHave, 0, false) +func verifyTestVectors(params float.Parameters, encoder *float.Encoder, decryptor *rlwe.Decryptor, valuesWant, valuesHave interface{}, t *testing.T) { + precStats := float.GetPrecisionStats(params, encoder, decryptor, valuesWant, valuesHave, 0, false) if *printPrecisionStats { t.Log(precStats.String()) } diff --git a/he/float/bootstrapper/bootstrapping/default_params.go b/he/float/bootstrapper/bootstrapping/default_params.go index c2809f83..d6f4a81b 100644 --- a/he/float/bootstrapper/bootstrapping/default_params.go +++ b/he/float/bootstrapper/bootstrapping/default_params.go @@ -1,13 +1,13 @@ package bootstrapping import ( - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/utils" ) type defaultParametersLiteral struct { - SchemeParams ckks.ParametersLiteral + SchemeParams float.ParametersLiteral BootstrappingParams ParametersLiteral } @@ -31,7 +31,7 @@ var ( // Precision : 26.6 bits for 2^{15} slots. // Failure : 2^{-138.7} for 2^{15} slots. N16QP1546H192H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 16, LogQ: []int{60, 40, 40, 40, 40, 40, 40, 40, 40, 40}, LogP: []int{61, 61, 61, 61, 61}, @@ -49,7 +49,7 @@ var ( // Precision : 32.1 bits for 2^{15} slots. // Failure : 2^{-138.7} for 2^{15} slots. N16QP1547H192H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 16, LogQ: []int{60, 45, 45, 45, 45, 45}, LogP: []int{61, 61, 61, 61}, @@ -72,7 +72,7 @@ var ( // Precision : 19.1 bits for 2^{15} slots. // Failure : 2^{-138.7} for 2^{15} slots. N16QP1553H192H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 16, LogQ: []int{55, 60, 60, 60, 60, 60, 60, 60}, LogP: []int{61, 61, 61, 61, 61}, @@ -94,7 +94,7 @@ var ( // Precision : 15.4 bits for 2^{14} slots. // Failure : 2^{-139.7} for 2^{14} slots. N15QP768H192H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 15, LogQ: []int{33, 50, 25}, LogP: []int{51, 51}, @@ -116,7 +116,7 @@ var ( // Precision : 23.8 bits for 2^{15} slots. // Failure : 2^{-138.7} for 2^{15} slots. N16QP1767H32768H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 16, LogQ: []int{60, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, LogP: []int{61, 61, 61, 61, 61, 61}, @@ -134,7 +134,7 @@ var ( // Precision : 29.8 bits for 2^{15} slots. // Failure : 2^{-138.7} for 2^{15} slots. N16QP1788H32768H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 16, LogQ: []int{60, 45, 45, 45, 45, 45, 45, 45, 45, 45}, LogP: []int{61, 61, 61, 61, 61}, @@ -157,7 +157,7 @@ var ( // Precision : 17.8 bits for 2^{15} slots. // Failure : 2^{-138.7} for 2^{15} slots. N16QP1793H32768H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 16, LogQ: []int{55, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 30}, LogP: []int{61, 61, 61, 61, 61}, @@ -179,7 +179,7 @@ var ( // Precision : 17.3 bits for 2^{14} slots. // Failure : 2^{-139.7} for 2^{14} slots. N15QP880H16384H32 = defaultParametersLiteral{ - ckks.ParametersLiteral{ + float.ParametersLiteral{ LogN: 15, LogQ: []int{40, 31, 31, 31, 31}, LogP: []int{56, 56}, diff --git a/he/float/bootstrapper/bootstrapping/parameters.go b/he/float/bootstrapper/bootstrapping/parameters.go index fe4f3d61..60285faa 100644 --- a/he/float/bootstrapper/bootstrapping/parameters.go +++ b/he/float/bootstrapper/bootstrapping/parameters.go @@ -6,7 +6,6 @@ import ( "github.com/google/go-cmp/cmp" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/utils" @@ -15,7 +14,7 @@ import ( // Parameters is a struct storing the parameters // of the bootstrapping circuit. type Parameters struct { - ckks.Parameters + float.Parameters SlotsToCoeffsParameters float.DFTMatrixLiteral Mod1ParametersLiteral float.Mod1ParametersLiteral CoeffsToSlotsParameters float.DFTMatrixLiteral @@ -23,21 +22,21 @@ type Parameters struct { EphemeralSecretWeight int // Hamming weight of the ephemeral secret. If 0, no ephemeral secret is used during the bootstrapping. } -// NewParametersFromLiteral instantiates a bootstrapping.Parameters from the residual ckks.Parameters and +// NewParametersFromLiteral instantiates a bootstrapping.Parameters from the residual float.Parameters and // a bootstrapping.ParametersLiteral struct. // -// The residualParameters corresponds to the ckks.Parameters that are left after the bootstrapping circuit is evaluated. +// The residualParameters corresponds to the float.Parameters that are left after the bootstrapping circuit is evaluated. // These are entirely independent of the bootstrapping parameters with one exception: the ciphertext primes Qi must be // congruent to 1 mod 2N of the bootstrapping parameters (note that the auxiliary primes Pi do not need to be). // This is required because the primes Qi of the residual parameters and the bootstrapping parameters are the same between // the two sets of parameters. // -// The user can ensure that this condition is met by setting the appropriate LogNThRoot in the ckks.ParametersLiteral before +// The user can ensure that this condition is met by setting the appropriate LogNThRoot in the float.ParametersLiteral before // instantiating them. // -// The method NewParametersFromLiteral will automatically allocate the ckks.Parameters of the bootstrapping circuit based on +// The method NewParametersFromLiteral will automatically allocate the float.Parameters of the bootstrapping circuit based on // the provided residualParameters and the information given in the bootstrapping.ParametersLiteral. -func NewParametersFromLiteral(residualParameters ckks.Parameters, btpLit ParametersLiteral) (Parameters, error) { +func NewParametersFromLiteral(residualParameters float.Parameters, btpLit ParametersLiteral) (Parameters, error) { var err error @@ -303,8 +302,8 @@ func NewParametersFromLiteral(residualParameters ckks.Parameters, btpLit Paramet primesNew[logpi] = primesNew[logpi][1:] } - // Instantiates the ckks.Parameters of the bootstrapping circuit. - params, err := ckks.NewParametersFromLiteral(ckks.ParametersLiteral{ + // Instantiates the float.Parameters of the bootstrapping circuit. + params, err := float.NewParametersFromLiteral(float.ParametersLiteral{ LogN: LogN, Q: Q, P: P, @@ -348,17 +347,17 @@ func (p Parameters) LogMaxSlots() int { return p.SlotsToCoeffsParameters.LogSlots } -// DepthCoeffsToSlots returns the depth of the Coeffs to Slots of the CKKS bootstrapping. +// DepthCoeffsToSlots returns the depth of the Coeffs to Slots of the bootstrapping. func (p Parameters) DepthCoeffsToSlots() (depth int) { return p.SlotsToCoeffsParameters.Depth(true) } -// DepthEvalMod returns the depth of the EvalMod step of the CKKS bootstrapping. +// DepthEvalMod returns the depth of the EvalMod step of the bootstrapping. func (p Parameters) DepthEvalMod() (depth int) { return p.Mod1ParametersLiteral.Depth() } -// DepthSlotsToCoeffs returns the depth of the Slots to Coeffs step of the CKKS bootstrapping. +// DepthSlotsToCoeffs returns the depth of the Slots to Coeffs step of the bootstrapping. func (p Parameters) DepthSlotsToCoeffs() (depth int) { return p.CoeffsToSlotsParameters.Depth(true) } @@ -382,7 +381,7 @@ func (p *Parameters) UnmarshalBinary(data []byte) (err error) { func (p Parameters) MarshalJSON() (data []byte, err error) { return json.Marshal(struct { - Parameters ckks.Parameters + Parameters float.Parameters SlotsToCoeffsParameters float.DFTMatrixLiteral Mod1ParametersLiteral float.Mod1ParametersLiteral CoeffsToSlotsParameters float.DFTMatrixLiteral @@ -400,7 +399,7 @@ func (p Parameters) MarshalJSON() (data []byte, err error) { func (p *Parameters) UnmarshalJSON(data []byte) (err error) { var params struct { - Parameters ckks.Parameters + Parameters float.Parameters SlotsToCoeffsParameters float.DFTMatrixLiteral Mod1ParametersLiteral float.Mod1ParametersLiteral CoeffsToSlotsParameters float.DFTMatrixLiteral @@ -423,7 +422,7 @@ func (p *Parameters) UnmarshalJSON(data []byte) (err error) { } // GaloisElements returns the list of Galois elements required to evaluate the bootstrapping. -func (p Parameters) GaloisElements(params ckks.Parameters) (galEls []uint64) { +func (p Parameters) GaloisElements(params float.Parameters) (galEls []uint64) { logN := params.LogN() diff --git a/he/float/bootstrapper/bootstrapping/parameters_literal.go b/he/float/bootstrapper/bootstrapping/parameters_literal.go index 31ddf23f..8fa14263 100644 --- a/he/float/bootstrapper/bootstrapping/parameters_literal.go +++ b/he/float/bootstrapper/bootstrapping/parameters_literal.go @@ -102,7 +102,7 @@ import ( // When using a small ratio (i.e. 2^4), for example if ct.PlaintextScale is close to Q[0] is small or if |m| is large, the Mod1InvDegree can be set to // a non zero value (i.e. 5 or 7). This will greatly improve the precision of the bootstrapping, at the expense of slightly increasing its depth. // -// Mod1Type: the type of approximation for the modular reduction polynomial. By default set to ckks.CosDiscrete. +// Mod1Type: the type of approximation for the modular reduction polynomial. By default set to float.CosDiscrete. // // K: the range of the approximation interval, by default set to 16. // @@ -122,7 +122,7 @@ type ParametersLiteral struct { EvalModLogScale *int // Default: 60 EphemeralSecretWeight *int // Default: 32 IterationsParameters *IterationsParameters // Default: nil (default starting level of 0 and 1 iteration) - Mod1Type float.Mod1Type // Default: ckks.CosDiscrete + Mod1Type float.Mod1Type // Default: float.CosDiscrete LogMessageRatio *int // Default: 8 K *int // Default: 16 Mod1Degree *int // Default: 30 diff --git a/he/float/bootstrapper/keys.go b/he/float/bootstrapper/keys.go index b46e41ff..9df57f93 100644 --- a/he/float/bootstrapper/keys.go +++ b/he/float/bootstrapper/keys.go @@ -3,7 +3,6 @@ package bootstrapper import ( "fmt" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float/bootstrapper/bootstrapping" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -71,7 +70,7 @@ func (b BootstrappingKeys) BinarySize() (dLen int) { // - These evaluation keys are generated under an ephemeral secret key skN2 using the distribution // specified in the bootstrapping parameters. // - The ephemeral key used to generate the bootstrapping keys is returned by this method for debugging purposes. -// - !WARNING! The bootstrapping parameters use their own and independent cryptographic parameters (i.e. ckks.Parameters) +// - !WARNING! The bootstrapping parameters use their own and independent cryptographic parameters (i.e. float.Parameters) // and it is the user's responsibility to ensure that these parameters meet the target security and tweak them if necessary. func (p Parameters) GenBootstrappingKeys(skN1 *rlwe.SecretKey) (btpkeys *BootstrappingKeys, skN2 *rlwe.SecretKey, err error) { @@ -80,7 +79,7 @@ func (p Parameters) GenBootstrappingKeys(skN1 *rlwe.SecretKey) (btpkeys *Bootstr var EvkCmplxToReal *rlwe.EvaluationKey paramsN2 := p.Parameters.Parameters - kgen := ckks.NewKeyGenerator(paramsN2) + kgen := rlwe.NewKeyGenerator(paramsN2) // Ephemeral secret-key used to generate the evaluation keys. skN2 = kgen.GenSecretKeyNew() diff --git a/he/float/bootstrapper/parameters.go b/he/float/bootstrapper/parameters.go index 3fa41e96..51196b33 100644 --- a/he/float/bootstrapper/parameters.go +++ b/he/float/bootstrapper/parameters.go @@ -1,7 +1,7 @@ package bootstrapper import ( - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/he/float/bootstrapper/bootstrapping" ) @@ -13,21 +13,21 @@ type ParametersLiteral bootstrapping.ParametersLiteral // See bootstrapping.Parameters for additional information. type Parameters struct { bootstrapping.Parameters - ResidualParameters ckks.Parameters + ResidualParameters float.Parameters } // NewParametersFromLiteral is a wrapper of bootstrapping.NewParametersFromLiteral. // See bootstrapping.NewParametersFromLiteral for additional information. // // >>>>>>>!WARNING!<<<<<<< -// The bootstrapping parameters use their own and independent cryptographic parameters (i.e. ckks.Parameters) +// The bootstrapping parameters use their own and independent cryptographic parameters (i.e. float.Parameters) // which are instantiated based on the option specified in `paramsBootstrapping` (and the default values of // bootstrapping.Parameters). // It is the user's responsibility to ensure that these scheme parameters meet the target security and to tweak them // if necessary. // It is possible to access information about these cryptographic parameters directly through the -// instantiated bootstrapper.Parameters struct which supports and API an identical to the ckks.Parameters. -func NewParametersFromLiteral(paramsResidual ckks.Parameters, paramsBootstrapping ParametersLiteral) (Parameters, error) { +// instantiated bootstrapper.Parameters struct which supports and API an identical to the float.Parameters. +func NewParametersFromLiteral(paramsResidual float.Parameters, paramsBootstrapping ParametersLiteral) (Parameters, error) { params, err := bootstrapping.NewParametersFromLiteral(paramsResidual, bootstrapping.ParametersLiteral(paramsBootstrapping)) return Parameters{ Parameters: params, diff --git a/he/float/bootstrapper/sk_bootstrapper.go b/he/float/bootstrapper/sk_bootstrapper.go index 2d923f4a..4da383ef 100644 --- a/he/float/bootstrapper/sk_bootstrapper.go +++ b/he/float/bootstrapper/sk_bootstrapper.go @@ -1,7 +1,7 @@ package bootstrapper import ( - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils/bignum" ) @@ -9,8 +9,8 @@ import ( // SecretKeyBootstrapper is an implementation of the rlwe.Bootstrapping interface that // uses the secret-key to decrypt and re-encrypt the bootstrapped ciphertext. type SecretKeyBootstrapper struct { - ckks.Parameters - *ckks.Encoder + float.Parameters + *float.Encoder *rlwe.Decryptor *rlwe.Encryptor sk *rlwe.SecretKey @@ -19,12 +19,12 @@ type SecretKeyBootstrapper struct { MinLevel int } -func NewSecretKeyBootstrapper(params ckks.Parameters, sk *rlwe.SecretKey) *SecretKeyBootstrapper { +func NewSecretKeyBootstrapper(params float.Parameters, sk *rlwe.SecretKey) *SecretKeyBootstrapper { return &SecretKeyBootstrapper{ Parameters: params, - Encoder: ckks.NewEncoder(params), - Decryptor: ckks.NewDecryptor(params, sk), - Encryptor: ckks.NewEncryptor(params, sk), + Encoder: float.NewEncoder(params), + Decryptor: rlwe.NewDecryptor(params, sk), + Encryptor: rlwe.NewEncryptor(params, sk), sk: sk, Values: make([]*bignum.Complex, params.N())} } @@ -34,7 +34,7 @@ func (d *SecretKeyBootstrapper) Bootstrap(ct *rlwe.Ciphertext) (*rlwe.Ciphertext if err := d.Decode(d.DecryptNew(ct), values); err != nil { return nil, err } - pt := ckks.NewPlaintext(d.Parameters, d.MaxLevel()) + pt := float.NewPlaintext(d.Parameters, d.MaxLevel()) pt.MetaData = ct.MetaData pt.Scale = d.Parameters.DefaultScale() if err := d.Encode(values, pt); err != nil { diff --git a/he/float/bootstrapper/utils.go b/he/float/bootstrapper/utils.go index b9dff87f..40ce7b6d 100644 --- a/he/float/bootstrapper/utils.go +++ b/he/float/bootstrapper/utils.go @@ -4,14 +4,14 @@ import ( "fmt" "math/bits" - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils" ) func (b Bootstrapper) SwitchRingDegreeN1ToN2New(ctN1 *rlwe.Ciphertext) (ctN2 *rlwe.Ciphertext) { - ctN2 = ckks.NewCiphertext(b.Parameters.Parameters.Parameters, 1, ctN1.Level()) + ctN2 = float.NewCiphertext(b.Parameters.Parameters.Parameters, 1, ctN1.Level()) // Sanity check, this error should never happen unless this algorithm has been improperly // modified to pass invalid inputs. @@ -22,7 +22,7 @@ func (b Bootstrapper) SwitchRingDegreeN1ToN2New(ctN1 *rlwe.Ciphertext) (ctN2 *rl } func (b Bootstrapper) SwitchRingDegreeN2ToN1New(ctN2 *rlwe.Ciphertext) (ctN1 *rlwe.Ciphertext) { - ctN1 = ckks.NewCiphertext(b.ResidualParameters, 1, ctN2.Level()) + ctN1 = float.NewCiphertext(b.ResidualParameters, 1, ctN2.Level()) // Sanity check, this error should never happen unless this algorithm has been improperly // modified to pass invalid inputs. @@ -33,22 +33,22 @@ func (b Bootstrapper) SwitchRingDegreeN2ToN1New(ctN2 *rlwe.Ciphertext) (ctN1 *rl } func (b Bootstrapper) ComplexToRealNew(ctCmplx *rlwe.Ciphertext) (ctReal *rlwe.Ciphertext) { - ctReal = ckks.NewCiphertext(b.ResidualParameters, 1, ctCmplx.Level()) + ctReal = float.NewCiphertext(b.ResidualParameters, 1, ctCmplx.Level()) // Sanity check, this error should never happen unless this algorithm has been improperly // modified to pass invalid inputs. - if err := b.bridge.ComplexToReal(b.bootstrapper.Evaluator, ctCmplx, ctReal); err != nil { + if err := b.bridge.ComplexToReal(&b.bootstrapper.Evaluator.Evaluator, ctCmplx, ctReal); err != nil { panic(err) } return } func (b Bootstrapper) RealToComplexNew(ctReal *rlwe.Ciphertext) (ctCmplx *rlwe.Ciphertext) { - ctCmplx = ckks.NewCiphertext(b.Parameters.Parameters.Parameters, 1, ctReal.Level()) + ctCmplx = float.NewCiphertext(b.Parameters.Parameters.Parameters, 1, ctReal.Level()) // Sanity check, this error should never happen unless this algorithm has been improperly // modified to pass invalid inputs. - if err := b.bridge.RealToComplex(b.bootstrapper.Evaluator, ctReal, ctCmplx); err != nil { + if err := b.bridge.RealToComplex(&b.bootstrapper.Evaluator.Evaluator, ctReal, ctCmplx); err != nil { panic(err) } return @@ -94,7 +94,7 @@ func (b Bootstrapper) UnpackAndSwitchN2Tn1(cts []rlwe.Ciphertext, LogSlots, Nb i return cts, nil } -func (b Bootstrapper) UnPack(cts []rlwe.Ciphertext, params ckks.Parameters, LogSlots, Nb int, xPow2Inv []ring.Poly) ([]rlwe.Ciphertext, error) { +func (b Bootstrapper) UnPack(cts []rlwe.Ciphertext, params float.Parameters, LogSlots, Nb int, xPow2Inv []ring.Poly) ([]rlwe.Ciphertext, error) { LogGap := params.LogMaxSlots() - LogSlots if LogGap == 0 { @@ -132,7 +132,7 @@ func (b Bootstrapper) UnPack(cts []rlwe.Ciphertext, params ckks.Parameters, LogS return cts, nil } -func (b Bootstrapper) Pack(cts []rlwe.Ciphertext, params ckks.Parameters, xPow2 []ring.Poly) ([]rlwe.Ciphertext, error) { +func (b Bootstrapper) Pack(cts []rlwe.Ciphertext, params float.Parameters, xPow2 []ring.Poly) ([]rlwe.Ciphertext, error) { var LogSlots = cts[0].LogSlots() RingDegree := params.N() diff --git a/he/float/comparisons.go b/he/float/comparisons.go index 3caf8cbd..aad334ba 100644 --- a/he/float/comparisons.go +++ b/he/float/comparisons.go @@ -3,7 +3,6 @@ package float import ( "math/big" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils" @@ -18,7 +17,7 @@ type ComparisonEvaluator struct { } // NewComparisonEvaluator instantiates a new ComparisonEvaluator. -// The default ckks.Evaluator is compliant with the EvaluatorForMinimaxCompositePolynomial interface. +// The default float.Evaluator is compliant with the EvaluatorForMinimaxCompositePolynomial interface. // The field he.Bootstrapper[rlwe.Ciphertext] can be nil if the parameters have enough level to support the computation. // // Giving a MinimaxCompositePolynomial is optional, but it is highly recommended to provide one that is optimized @@ -33,7 +32,7 @@ type ComparisonEvaluator struct { // See the doc of DefaultMinimaxCompositePolynomialForSign for additional information about the performance of this approximation. // // This method is allocation free if a MinimaxCompositePolynomial is given. -func NewComparisonEvaluator(params ckks.Parameters, eval EvaluatorForMinimaxCompositePolynomial, bootstrapper he.Bootstrapper[rlwe.Ciphertext], signPoly ...MinimaxCompositePolynomial) *ComparisonEvaluator { +func NewComparisonEvaluator(params Parameters, eval EvaluatorForMinimaxCompositePolynomial, bootstrapper he.Bootstrapper[rlwe.Ciphertext], signPoly ...MinimaxCompositePolynomial) *ComparisonEvaluator { if len(signPoly) == 1 { return &ComparisonEvaluator{*NewMinimaxCompositePolynomialEvaluator(params, eval, bootstrapper), signPoly[0]} } else { diff --git a/he/float/comparisons_test.go b/he/float/comparisons_test.go index 0af966bc..aa996fcd 100644 --- a/he/float/comparisons_test.go +++ b/he/float/comparisons_test.go @@ -4,7 +4,6 @@ import ( "math/big" "testing" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/he/float/bootstrapper" "github.com/tuneinsight/lattigo/v4/ring" @@ -25,11 +24,11 @@ func TestComparisons(t *testing.T) { paramsLiteral.LogN = 10 } - params, err := ckks.NewParametersFromLiteral(paramsLiteral) + params, err := float.NewParametersFromLiteral(paramsLiteral) require.NoError(t, err) - var tc *ckksTestContext - if tc, err = genCKKSTestParams(params); err != nil { + var tc *testContext + if tc, err = genTestParams(params); err != nil { t.Fatal(err) } @@ -54,7 +53,7 @@ func TestComparisons(t *testing.T) { t.Run(GetTestName(params, "Sign"), func(t *testing.T) { - values, _, ct := newCKKSTestVectors(tc, enc, complex(-1, 0), complex(1, 0), t) + values, _, ct := newTestVectors(tc, enc, complex(-1, 0), complex(1, 0), t) var sign *rlwe.Ciphertext sign, err = CmpEval.Sign(ct) @@ -70,12 +69,12 @@ func TestComparisons(t *testing.T) { want[i] = polys.Evaluate(values[i])[0] } - ckks.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "Step"), func(t *testing.T) { - values, _, ct := newCKKSTestVectors(tc, enc, complex(-1, 0), complex(1, 0), t) + values, _, ct := newTestVectors(tc, enc, complex(-1, 0), complex(1, 0), t) var step *rlwe.Ciphertext step, err = CmpEval.Step(ct) @@ -95,13 +94,13 @@ func TestComparisons(t *testing.T) { want[i].Add(want[i], half) } - ckks.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "Max"), func(t *testing.T) { - values0, _, ct0 := newCKKSTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) - values1, _, ct1 := newCKKSTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) + values0, _, ct0 := newTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) + values1, _, ct1 := newTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) var max *rlwe.Ciphertext max, err = CmpEval.Max(ct0, ct1) @@ -122,13 +121,13 @@ func TestComparisons(t *testing.T) { } } - ckks.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "Min"), func(t *testing.T) { - values0, _, ct0 := newCKKSTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) - values1, _, ct1 := newCKKSTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) + values0, _, ct0 := newTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) + values1, _, ct1 := newTestVectors(tc, enc, complex(-0.5, 0), complex(0.5, 0), t) var max *rlwe.Ciphertext max, err = CmpEval.Min(ct0, ct1) @@ -149,7 +148,7 @@ func TestComparisons(t *testing.T) { } } - ckks.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) } } diff --git a/he/float/dft.go b/he/float/dft.go index 3ba5526d..c6182a15 100644 --- a/he/float/dft.go +++ b/he/float/dft.go @@ -15,7 +15,7 @@ import ( ) // EvaluatorForDFT is an interface defining the set of methods required to instantiate a DFTEvaluator. -// The default ckks.Evaluator is compliant to this interface. +// The default float.Evaluator is compliant to this interface. type EvaluatorForDFT interface { rlwe.ParameterProvider he.EvaluatorForLinearTransformation @@ -85,7 +85,7 @@ func (d DFTMatrixLiteral) Depth(actual bool) (depth int) { } // GaloisElements returns the list of rotations performed during the CoeffsToSlot operation. -func (d DFTMatrixLiteral) GaloisElements(params ckks.Parameters) (galEls []uint64) { +func (d DFTMatrixLiteral) GaloisElements(params Parameters) (galEls []uint64) { rotations := []int{} logSlots := d.LogSlots @@ -127,12 +127,12 @@ func (d *DFTMatrixLiteral) UnmarshalBinary(data []byte) error { type DFTEvaluator struct { EvaluatorForDFT *LinearTransformationEvaluator - parameters ckks.Parameters + parameters Parameters } // NewDFTEvaluator instantiates a new DFTEvaluator. -// The default ckks.Evaluator is compliant to the EvaluatorForDFT interface. -func NewDFTEvaluator(params ckks.Parameters, eval EvaluatorForDFT) *DFTEvaluator { +// The default float.Evaluator is compliant to the EvaluatorForDFT interface. +func NewDFTEvaluator(params Parameters, eval EvaluatorForDFT) *DFTEvaluator { dfteval := new(DFTEvaluator) dfteval.EvaluatorForDFT = eval dfteval.LinearTransformationEvaluator = NewLinearTransformationEvaluator(eval) @@ -141,7 +141,7 @@ func NewDFTEvaluator(params ckks.Parameters, eval EvaluatorForDFT) *DFTEvaluator } // NewDFTMatrixFromLiteral generates the factorized DFT/IDFT matrices for the homomorphic encoding/decoding. -func NewDFTMatrixFromLiteral(params ckks.Parameters, d DFTMatrixLiteral, encoder *ckks.Encoder) (DFTMatrix, error) { +func NewDFTMatrixFromLiteral(params Parameters, d DFTMatrixLiteral, encoder *Encoder) (DFTMatrix, error) { logSlots := d.LogSlots logdSlots := logSlots @@ -204,10 +204,10 @@ func NewDFTMatrixFromLiteral(params ckks.Parameters, d DFTMatrixLiteral, encoder // If the packing is sparse (n < N/2), then returns ctReal = Ecd(vReal || vImag) and ctImag = nil. // If the packing is dense (n == N/2), then returns ctReal = Ecd(vReal) and ctImag = Ecd(vImag). func (eval *DFTEvaluator) CoeffsToSlotsNew(ctIn *rlwe.Ciphertext, ctsMatrices DFTMatrix) (ctReal, ctImag *rlwe.Ciphertext, err error) { - ctReal = ckks.NewCiphertext(eval.parameters, 1, ctsMatrices.LevelStart) + ctReal = NewCiphertext(eval.parameters, 1, ctsMatrices.LevelStart) if ctsMatrices.LogSlots == eval.parameters.LogMaxSlots() { - ctImag = ckks.NewCiphertext(eval.parameters, 1, ctsMatrices.LevelStart) + ctImag = NewCiphertext(eval.parameters, 1, ctsMatrices.LevelStart) } return ctReal, ctImag, eval.CoeffsToSlots(ctIn, ctsMatrices, ctReal, ctImag) @@ -293,7 +293,7 @@ func (eval *DFTEvaluator) SlotsToCoeffsNew(ctReal, ctImag *rlwe.Ciphertext, stcM return nil, fmt.Errorf("ctReal.Level() or ctImag.Level() < DFTMatrix.LevelStart") } - opOut = ckks.NewCiphertext(eval.parameters, 1, stcMatrices.LevelStart) + opOut = NewCiphertext(eval.parameters, 1, stcMatrices.LevelStart) return opOut, eval.SlotsToCoeffs(ctReal, ctImag, stcMatrices, opOut) } diff --git a/he/float/dft_test.go b/he/float/dft_test.go index d1b9f7c9..97135aa4 100644 --- a/he/float/dft_test.go +++ b/he/float/dft_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -27,13 +26,13 @@ func TestHomomorphicDFT(t *testing.T) { for _, paramsLiteral := range testParametersLiteral { - var params ckks.Parameters - if params, err = ckks.NewParametersFromLiteral(paramsLiteral); err != nil { + var params float.Parameters + if params, err = float.NewParametersFromLiteral(paramsLiteral); err != nil { t.Fatal(err) } for _, logSlots := range []int{params.LogMaxDimensions().Cols - 1, params.LogMaxDimensions().Cols} { - for _, testSet := range []func(params ckks.Parameters, logSlots int, t *testing.T){ + for _, testSet := range []func(params float.Parameters, logSlots int, t *testing.T){ testHomomorphicEncoding, testHomomorphicDecoding, } { @@ -67,7 +66,7 @@ func testDFTMatrixLiteralMarshalling(t *testing.T) { }) } -func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) { +func testHomomorphicEncoding(params float.Parameters, LogSlots int, t *testing.T) { slots := 1 << LogSlots @@ -78,9 +77,9 @@ func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) packing = "SparsePacking" } - var params2N ckks.Parameters + var params2N float.Parameters var err error - if params2N, err = ckks.NewParametersFromLiteral(ckks.ParametersLiteral{ + if params2N, err = float.NewParametersFromLiteral(float.ParametersLiteral{ LogN: params.LogN() + 1, LogQ: []int{60}, LogP: []int{61}, @@ -89,7 +88,7 @@ func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) t.Fatal(err) } - ecd2N := ckks.NewEncoder(params2N) + ecd2N := float.NewEncoder(params2N) t.Run("Encode/"+packing, func(t *testing.T) { @@ -125,11 +124,11 @@ func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) Levels: Levels, } - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) sk := kgen.GenSecretKeyNew() - encoder := ckks.NewEncoder(params, 90) // Required to force roots.(type) to be []*bignum.Complex instead of []complex128 - encryptor := ckks.NewEncryptor(params, sk) - decryptor := ckks.NewDecryptor(params, sk) + encoder := float.NewEncoder(params, 90) // Required to force roots.(type) to be []*bignum.Complex instead of []complex128 + encryptor := rlwe.NewEncryptor(params, sk) + decryptor := rlwe.NewDecryptor(params, sk) // Generates the encoding matrices CoeffsToSlotMatrices, err := float.NewDFTMatrixFromLiteral(params, CoeffsToSlotsParametersLiteral, encoder) @@ -143,7 +142,7 @@ func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) evk := rlwe.NewMemEvaluationKeySet(nil, kgen.GenGaloisKeysNew(galEls, sk)...) // Creates an evaluator with the rotation keys - eval := ckks.NewEvaluator(params, evk) + eval := float.NewEvaluator(params, evk) hdftEval := float.NewDFTEvaluator(params, eval) prec := params.EncodingPrecision() @@ -181,7 +180,7 @@ func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) } // Encodes coefficient-wise and encrypts the test vector - pt := ckks.NewPlaintext(params, params.MaxLevel()) + pt := float.NewPlaintext(params, params.MaxLevel()) pt.LogDimensions = ring.Dimensions{Rows: 0, Cols: LogSlots} pt.IsBatched = false @@ -232,7 +231,7 @@ func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) } // Compares - ckks.VerifyTestVectors(params, ecd2N, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd2N, nil, want, have, params.LogDefaultScale(), 0, *printPrecisionStats, t) } else { @@ -276,13 +275,13 @@ func testHomomorphicEncoding(params ckks.Parameters, LogSlots int, t *testing.T) wantImag[i], wantImag[j] = vec1[i][0], vec1[i][1] } - ckks.VerifyTestVectors(params, ecd2N, nil, wantReal, haveReal, params.LogDefaultScale(), 0, *printPrecisionStats, t) - ckks.VerifyTestVectors(params, ecd2N, nil, wantImag, haveImag, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd2N, nil, wantReal, haveReal, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd2N, nil, wantImag, haveImag, params.LogDefaultScale(), 0, *printPrecisionStats, t) } }) } -func testHomomorphicDecoding(params ckks.Parameters, LogSlots int, t *testing.T) { +func testHomomorphicDecoding(params float.Parameters, LogSlots int, t *testing.T) { slots := 1 << LogSlots @@ -329,11 +328,11 @@ func testHomomorphicDecoding(params ckks.Parameters, LogSlots int, t *testing.T) Levels: Levels, } - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) sk := kgen.GenSecretKeyNew() - encoder := ckks.NewEncoder(params) - encryptor := ckks.NewEncryptor(params, sk) - decryptor := ckks.NewDecryptor(params, sk) + encoder := float.NewEncoder(params) + encryptor := rlwe.NewEncryptor(params, sk) + decryptor := rlwe.NewDecryptor(params, sk) // Generates the encoding matrices SlotsToCoeffsMatrix, err := float.NewDFTMatrixFromLiteral(params, SlotsToCoeffsParametersLiteral, encoder) @@ -347,7 +346,7 @@ func testHomomorphicDecoding(params ckks.Parameters, LogSlots int, t *testing.T) evk := rlwe.NewMemEvaluationKeySet(nil, kgen.GenGaloisKeysNew(galEls, sk)...) // Creates an evaluator with the rotation keys - eval := ckks.NewEvaluator(params, evk) + eval := float.NewEvaluator(params, evk) hdftEval := float.NewDFTEvaluator(params, eval) prec := params.EncodingPrecision() @@ -375,7 +374,7 @@ func testHomomorphicDecoding(params ckks.Parameters, LogSlots int, t *testing.T) } // Encodes and encrypts the test vectors - plaintext := ckks.NewPlaintext(params, params.MaxLevel()) + plaintext := float.NewPlaintext(params, params.MaxLevel()) plaintext.LogDimensions = ring.Dimensions{Rows: 0, Cols: LogSlots} if err = encoder.Encode(valuesReal, plaintext); err != nil { t.Fatal(err) @@ -424,6 +423,6 @@ func testHomomorphicDecoding(params ckks.Parameters, LogSlots int, t *testing.T) // Result is bit-reversed, so applies the bit-reverse permutation on the reference vector utils.BitReverseInPlaceSlice(valuesReal, slots) - ckks.VerifyTestVectors(params, encoder, decryptor, valuesReal, valuesTest, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, encoder, decryptor, valuesReal, valuesTest, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) } diff --git a/he/float/float.go b/he/float/float.go index 405de9d4..7bf6c9b8 100644 --- a/he/float/float.go +++ b/he/float/float.go @@ -1,10 +1,100 @@ -// Package float implements Homomorphic Encryption for encrypted arithmetic over floating point numbers. +// Package float implements Homomorphic Encryption for fixed-point approximate arithmetic over the reals/complexes. package float import ( + "testing" + "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/rlwe" ) type Float interface { ckks.Float } + +type ParametersLiteral ckks.ParametersLiteral + +func NewParametersFromLiteral(paramsLit ParametersLiteral) (Parameters, error) { + params, err := ckks.NewParametersFromLiteral(ckks.ParametersLiteral(paramsLit)) + return Parameters{Parameters: params}, err +} + +type Parameters struct { + ckks.Parameters +} + +func (p Parameters) MarshalJSON() (d []byte, err error) { + return p.Parameters.MarshalJSON() +} + +func (p *Parameters) UnmarshalJSON(d []byte) (err error) { + return p.Parameters.UnmarshalJSON(d) +} + +func (p Parameters) MarshalBinary() (d []byte, err error) { + return p.Parameters.MarshalBinary() +} + +func (p *Parameters) UnmarshalBinary(d []byte) (err error) { + return p.Parameters.UnmarshalBinary(d) +} + +func (p Parameters) Equal(other *Parameters) bool { + return p.Parameters.Equal(&other.Parameters) +} + +func NewPlaintext(params Parameters, level int) *rlwe.Plaintext { + return ckks.NewPlaintext(params.Parameters, level) +} + +func NewCiphertext(params Parameters, degree, level int) *rlwe.Ciphertext { + return ckks.NewCiphertext(params.Parameters, degree, level) +} + +type Encoder struct { + ckks.Encoder +} + +func NewEncoder(params Parameters, prec ...uint) *Encoder { + + var ecd *ckks.Encoder + if len(prec) == 0 { + ecd = ckks.NewEncoder(params.Parameters) + } else { + ecd = ckks.NewEncoder(params.Parameters, prec[0]) + } + + return &Encoder{Encoder: *ecd} +} + +func (ecd Encoder) ShallowCopy() *Encoder { + return &Encoder{Encoder: *ecd.Encoder.ShallowCopy()} +} + +type Evaluator struct { + ckks.Evaluator +} + +func NewEvaluator(params Parameters, evk rlwe.EvaluationKeySet) *Evaluator { + return &Evaluator{Evaluator: *ckks.NewEvaluator(params.Parameters, evk)} +} + +func (eval Evaluator) GetParameters() *Parameters { + return &Parameters{*eval.Evaluator.GetParameters()} +} + +func (eval Evaluator) WithKey(evk rlwe.EvaluationKeySet) *Evaluator { + return &Evaluator{Evaluator: *eval.Evaluator.WithKey(evk)} +} + +func (eval Evaluator) ShallowCopy() *Evaluator { + return &Evaluator{Evaluator: *eval.Evaluator.ShallowCopy()} +} + +func GetPrecisionStats(params Parameters, encoder *Encoder, decryptor *rlwe.Decryptor, want, have interface{}, logprec float64, computeDCF bool) (prec ckks.PrecisionStats) { + return ckks.GetPrecisionStats(params.Parameters, &encoder.Encoder, decryptor, want, have, logprec, computeDCF) +} + +func VerifyTestVectors(params Parameters, encoder *Encoder, decryptor *rlwe.Decryptor, valuesWant, valuesHave interface{}, log2MinPrec int, logprec float64, printPrecisionStats bool, t *testing.T) { + ckks.VerifyTestVectors(params.Parameters, &encoder.Encoder, decryptor, valuesWant, valuesHave, log2MinPrec, logprec, printPrecisionStats, t) +} diff --git a/he/float/float_test.go b/he/float/float_test.go index a893fd8c..e177239e 100644 --- a/he/float/float_test.go +++ b/he/float/float_test.go @@ -10,7 +10,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -22,7 +21,7 @@ import ( var flagParamString = flag.String("params", "", "specify the test cryptographic parameters as a JSON string. Overrides -short.") var printPrecisionStats = flag.Bool("print-precision", false, "print precision stats") -func GetTestName(params ckks.Parameters, opname string) string { +func GetTestName(params float.Parameters, opname string) string { return fmt.Sprintf("%s/RingType=%s/logN=%d/logQP=%d/Qi=%d/Pi=%d/LogScale=%d", opname, params.RingType(), @@ -33,29 +32,29 @@ func GetTestName(params ckks.Parameters, opname string) string { int(math.Log2(params.DefaultScale().Float64()))) } -type ckksTestContext struct { - params ckks.Parameters +type testContext struct { + params float.Parameters ringQ *ring.Ring ringP *ring.Ring prng sampling.PRNG - encoder *ckks.Encoder + encoder *float.Encoder kgen *rlwe.KeyGenerator sk *rlwe.SecretKey pk *rlwe.PublicKey encryptorPk *rlwe.Encryptor encryptorSk *rlwe.Encryptor decryptor *rlwe.Decryptor - evaluator *ckks.Evaluator + evaluator *float.Evaluator } func TestFloat(t *testing.T) { var err error - var testParams []ckks.ParametersLiteral + var testParams []float.ParametersLiteral switch { case *flagParamString != "": // the custom test suite reads the parameters from the -params flag - testParams = append(testParams, ckks.ParametersLiteral{}) + testParams = append(testParams, float.ParametersLiteral{}) if err = json.Unmarshal([]byte(*flagParamString), &testParams[0]); err != nil { t.Fatal(err) } @@ -73,18 +72,18 @@ func TestFloat(t *testing.T) { paramsLiteral.LogN = 10 } - var params ckks.Parameters - if params, err = ckks.NewParametersFromLiteral(paramsLiteral); err != nil { + var params float.Parameters + if params, err = float.NewParametersFromLiteral(paramsLiteral); err != nil { t.Fatal(err) } - var tc *ckksTestContext - if tc, err = genCKKSTestParams(params); err != nil { + var tc *testContext + if tc, err = genTestParams(params); err != nil { t.Fatal(err) } - for _, testSet := range []func(tc *ckksTestContext, t *testing.T){ - testCKKSLinearTransformation, + for _, testSet := range []func(tc *testContext, t *testing.T){ + testLinearTransformation, testEvaluatePolynomial, } { testSet(tc, t) @@ -94,13 +93,13 @@ func TestFloat(t *testing.T) { } } -func genCKKSTestParams(defaultParam ckks.Parameters) (tc *ckksTestContext, err error) { +func genTestParams(defaultParam float.Parameters) (tc *testContext, err error) { - tc = new(ckksTestContext) + tc = new(testContext) tc.params = defaultParam - tc.kgen = ckks.NewKeyGenerator(tc.params) + tc.kgen = rlwe.NewKeyGenerator(tc.params) tc.sk, tc.pk = tc.kgen.GenKeyPairNew() @@ -113,24 +112,24 @@ func genCKKSTestParams(defaultParam ckks.Parameters) (tc *ckksTestContext, err e return nil, err } - tc.encoder = ckks.NewEncoder(tc.params) + tc.encoder = float.NewEncoder(tc.params) - tc.encryptorPk = ckks.NewEncryptor(tc.params, tc.pk) - tc.encryptorSk = ckks.NewEncryptor(tc.params, tc.sk) - tc.decryptor = ckks.NewDecryptor(tc.params, tc.sk) - tc.evaluator = ckks.NewEvaluator(tc.params, rlwe.NewMemEvaluationKeySet(tc.kgen.GenRelinearizationKeyNew(tc.sk))) + tc.encryptorPk = rlwe.NewEncryptor(tc.params, tc.pk) + tc.encryptorSk = rlwe.NewEncryptor(tc.params, tc.sk) + tc.decryptor = rlwe.NewDecryptor(tc.params, tc.sk) + tc.evaluator = float.NewEvaluator(tc.params, rlwe.NewMemEvaluationKeySet(tc.kgen.GenRelinearizationKeyNew(tc.sk))) return tc, nil } -func newCKKSTestVectors(tc *ckksTestContext, encryptor *rlwe.Encryptor, a, b complex128, t *testing.T) (values []*bignum.Complex, pt *rlwe.Plaintext, ct *rlwe.Ciphertext) { +func newTestVectors(tc *testContext, encryptor *rlwe.Encryptor, a, b complex128, t *testing.T) (values []*bignum.Complex, pt *rlwe.Plaintext, ct *rlwe.Ciphertext) { var err error prec := tc.encoder.Prec() - pt = ckks.NewPlaintext(tc.params, tc.params.MaxLevel()) + pt = float.NewPlaintext(tc.params, tc.params.MaxLevel()) values = make([]*bignum.Complex, pt.Slots()) @@ -163,13 +162,13 @@ func newCKKSTestVectors(tc *ckksTestContext, encryptor *rlwe.Encryptor, a, b com return values, pt, ct } -func testCKKSLinearTransformation(tc *ckksTestContext, t *testing.T) { +func testLinearTransformation(tc *testContext, t *testing.T) { params := tc.params t.Run(GetTestName(params, "Average"), func(t *testing.T) { - values, _, ciphertext := newCKKSTestVectors(tc, tc.encryptorSk, -1-1i, 1+1i, t) + values, _, ciphertext := newTestVectors(tc, tc.encryptorSk, -1-1i, 1+1i, t) slots := ciphertext.Slots() @@ -202,12 +201,12 @@ func testCKKSLinearTransformation(tc *ckksTestContext, t *testing.T) { values[i][1].Quo(values[i][1], nB) } - ckks.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "LinearTransform/BSGS=True"), func(t *testing.T) { - values, _, ciphertext := newCKKSTestVectors(tc, tc.encryptorSk, -1-1i, 1+1i, t) + values, _, ciphertext := newTestVectors(tc, tc.encryptorSk, -1-1i, 1+1i, t) slots := ciphertext.Slots() @@ -263,12 +262,12 @@ func testCKKSLinearTransformation(tc *ckksTestContext, t *testing.T) { values[i].Add(values[i], tmp[(i+15)%slots]) } - ckks.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "LinearTransform/BSGS=False"), func(t *testing.T) { - values, _, ciphertext := newCKKSTestVectors(tc, tc.encryptorSk, -1-1i, 1+1i, t) + values, _, ciphertext := newTestVectors(tc, tc.encryptorSk, -1-1i, 1+1i, t) slots := ciphertext.Slots() @@ -324,11 +323,11 @@ func testCKKSLinearTransformation(tc *ckksTestContext, t *testing.T) { values[i].Add(values[i], tmp[(i+15)%slots]) } - ckks.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) } -func testEvaluatePolynomial(tc *ckksTestContext, t *testing.T) { +func testEvaluatePolynomial(tc *testContext, t *testing.T) { params := tc.params @@ -342,7 +341,7 @@ func testEvaluatePolynomial(tc *ckksTestContext, t *testing.T) { t.Skip("skipping test for params max level < 3") } - values, _, ciphertext := newCKKSTestVectors(tc, tc.encryptorSk, -1, 1, t) + values, _, ciphertext := newTestVectors(tc, tc.encryptorSk, -1, 1, t) prec := tc.encoder.Prec() @@ -367,7 +366,7 @@ func testEvaluatePolynomial(tc *ckksTestContext, t *testing.T) { t.Fatal(err) } - ckks.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "Polynomial/PolyVector/Exp"), func(t *testing.T) { @@ -376,7 +375,7 @@ func testEvaluatePolynomial(tc *ckksTestContext, t *testing.T) { t.Skip("skipping test for params max level < 3") } - values, _, ciphertext := newCKKSTestVectors(tc, tc.encryptorSk, -1, 1, t) + values, _, ciphertext := newTestVectors(tc, tc.encryptorSk, -1, 1, t) prec := tc.encoder.Prec() @@ -415,6 +414,6 @@ func testEvaluatePolynomial(tc *ckksTestContext, t *testing.T) { t.Fatal(err) } - ckks.VerifyTestVectors(params, tc.encoder, tc.decryptor, valuesWant, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, tc.decryptor, valuesWant, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) } diff --git a/he/float/inverse.go b/he/float/inverse.go index 9e61c0e6..3e96c19a 100644 --- a/he/float/inverse.go +++ b/he/float/inverse.go @@ -4,7 +4,6 @@ import ( "fmt" "math" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils" @@ -12,7 +11,7 @@ import ( // EvaluatorForInverse defines a set of common and scheme agnostic // methods that are necessary to instantiate an InverseEvaluator. -// The default ckks.Evaluator is compliant to this interface. +// The default float.Evaluator is compliant to this interface. type EvaluatorForInverse interface { EvaluatorForMinimaxCompositePolynomial SetScale(ct *rlwe.Ciphertext, scale rlwe.Scale) (err error) @@ -24,14 +23,14 @@ type InverseEvaluator struct { EvaluatorForInverse *MinimaxCompositePolynomialEvaluator he.Bootstrapper[rlwe.Ciphertext] - Parameters ckks.Parameters + Parameters Parameters } // NewInverseEvaluator instantiates a new InverseEvaluator. -// The default ckks.Evaluator is compliant to the EvaluatorForInverse interface. +// The default float.Evaluator is compliant to the EvaluatorForInverse interface. // The field he.Bootstrapper[rlwe.Ciphertext] can be nil if the parameters have enough levels to support the computation. // This method is allocation free. -func NewInverseEvaluator(params ckks.Parameters, eval EvaluatorForInverse, btp he.Bootstrapper[rlwe.Ciphertext]) InverseEvaluator { +func NewInverseEvaluator(params Parameters, eval EvaluatorForInverse, btp he.Bootstrapper[rlwe.Ciphertext]) InverseEvaluator { return InverseEvaluator{ EvaluatorForInverse: eval, MinimaxCompositePolynomialEvaluator: NewMinimaxCompositePolynomialEvaluator(params, eval, btp), diff --git a/he/float/inverse_test.go b/he/float/inverse_test.go index a219ee2e..2aca9e87 100644 --- a/he/float/inverse_test.go +++ b/he/float/inverse_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/he/float/bootstrapper" "github.com/tuneinsight/lattigo/v4/ring" @@ -26,11 +25,11 @@ func TestInverse(t *testing.T) { paramsLiteral.LogN = 10 } - params, err := ckks.NewParametersFromLiteral(paramsLiteral) + params, err := float.NewParametersFromLiteral(paramsLiteral) require.NoError(t, err) - var tc *ckksTestContext - if tc, err = genCKKSTestParams(params); err != nil { + var tc *testContext + if tc, err = genTestParams(params); err != nil { t.Fatal(err) } @@ -62,7 +61,7 @@ func TestInverse(t *testing.T) { t.Run(GetTestName(params, "GoldschmidtDivisionNew"), func(t *testing.T) { - values, _, ciphertext := newCKKSTestVectors(tc, tc.encryptorSk, complex(min, 0), complex(2-min, 0), t) + values, _, ciphertext := newTestVectors(tc, tc.encryptorSk, complex(min, 0), complex(2-min, 0), t) one := new(big.Float).SetInt64(1) for i := range values { @@ -76,12 +75,12 @@ func TestInverse(t *testing.T) { t.Fatal(err) } - ckks.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, 70, 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, tc.decryptor, values, ciphertext, 70, 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "PositiveDomain"), func(t *testing.T) { - values, _, ct := newCKKSTestVectors(tc, enc, complex(0, 0), complex(max, 0), t) + values, _, ct := newTestVectors(tc, enc, complex(0, 0), complex(max, 0), t) invEval := float.NewInverseEvaluator(params, eval, btp) @@ -103,12 +102,12 @@ func TestInverse(t *testing.T) { } } - ckks.VerifyTestVectors(params, tc.encoder, nil, want, have, 70, 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, nil, want, have, 70, 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "NegativeDomain"), func(t *testing.T) { - values, _, ct := newCKKSTestVectors(tc, enc, complex(-max, 0), complex(0, 0), t) + values, _, ct := newTestVectors(tc, enc, complex(-max, 0), complex(0, 0), t) invEval := float.NewInverseEvaluator(params, eval, btp) @@ -130,12 +129,12 @@ func TestInverse(t *testing.T) { } } - ckks.VerifyTestVectors(params, tc.encoder, nil, want, have, 70, 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, nil, want, have, 70, 0, *printPrecisionStats, t) }) t.Run(GetTestName(params, "FullDomain"), func(t *testing.T) { - values, _, ct := newCKKSTestVectors(tc, enc, complex(-max, 0), complex(max, 0), t) + values, _, ct := newTestVectors(tc, enc, complex(-max, 0), complex(max, 0), t) invEval := float.NewInverseEvaluator(params, eval, btp) @@ -157,7 +156,7 @@ func TestInverse(t *testing.T) { } } - ckks.VerifyTestVectors(params, tc.encoder, nil, want, have, 70, 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, tc.encoder, nil, want, have, 70, 0, *printPrecisionStats, t) }) } } diff --git a/he/float/linear_transformation.go b/he/float/linear_transformation.go index 43b9eeff..a36cd7ea 100644 --- a/he/float/linear_transformation.go +++ b/he/float/linear_transformation.go @@ -1,7 +1,6 @@ package float import ( - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -9,7 +8,7 @@ import ( ) type floatEncoder[T Float, U ring.Poly | ringqp.Poly | *rlwe.Plaintext] struct { - *ckks.Encoder + *Encoder } func (e floatEncoder[T, U]) Encode(values []T, metadata *rlwe.MetaData, output U) (err error) { @@ -47,7 +46,7 @@ func NewLinearTransformation(params rlwe.ParameterProvider, lt LinearTransformat // EncodeLinearTransformation is a method used to encode EncodeLinearTransformation and a wrapper of he.EncodeLinearTransformation. // See he.EncodeLinearTransformation for the documentation. -func EncodeLinearTransformation[T Float](ecd *ckks.Encoder, diagonals Diagonals[T], allocated LinearTransformation) (err error) { +func EncodeLinearTransformation[T Float](ecd *Encoder, diagonals Diagonals[T], allocated LinearTransformation) (err error) { return he.EncodeLinearTransformation[T]( &floatEncoder[T, ringqp.Poly]{ecd}, he.Diagonals[T](diagonals), @@ -67,7 +66,7 @@ type LinearTransformationEvaluator struct { } // NewLinearTransformationEvaluator instantiates a new LinearTransformationEvaluator from a circuit.EvaluatorForLinearTransformation. -// The default ckks.Evaluator is compliant to the he.EvaluatorForLinearTransformation interface. +// The default float.Evaluator is compliant to the he.EvaluatorForLinearTransformation interface. // This method is allocation free. func NewLinearTransformationEvaluator(eval he.EvaluatorForLinearTransformation) (linTransEval *LinearTransformationEvaluator) { return &LinearTransformationEvaluator{ diff --git a/he/float/minimax_composite_polynomial_evaluator.go b/he/float/minimax_composite_polynomial_evaluator.go index ecb98530..51a59514 100644 --- a/he/float/minimax_composite_polynomial_evaluator.go +++ b/he/float/minimax_composite_polynomial_evaluator.go @@ -3,7 +3,6 @@ package float import ( "fmt" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -21,13 +20,13 @@ type MinimaxCompositePolynomialEvaluator struct { EvaluatorForMinimaxCompositePolynomial PolynomialEvaluator he.Bootstrapper[rlwe.Ciphertext] - Parameters ckks.Parameters + Parameters Parameters } // NewMinimaxCompositePolynomialEvaluator instantiates a new MinimaxCompositePolynomialEvaluator. -// The default ckks.Evaluator is compliant to the EvaluatorForMinimaxCompositePolynomial interface. +// The default float.Evaluator is compliant to the EvaluatorForMinimaxCompositePolynomial interface. // This method is allocation free. -func NewMinimaxCompositePolynomialEvaluator(params ckks.Parameters, eval EvaluatorForMinimaxCompositePolynomial, bootstrapper he.Bootstrapper[rlwe.Ciphertext]) *MinimaxCompositePolynomialEvaluator { +func NewMinimaxCompositePolynomialEvaluator(params Parameters, eval EvaluatorForMinimaxCompositePolynomial, bootstrapper he.Bootstrapper[rlwe.Ciphertext]) *MinimaxCompositePolynomialEvaluator { return &MinimaxCompositePolynomialEvaluator{eval, *NewPolynomialEvaluator(params, eval), bootstrapper, params} } diff --git a/he/float/mod1_evaluator.go b/he/float/mod1_evaluator.go index 2211a2ed..dd480a01 100644 --- a/he/float/mod1_evaluator.go +++ b/he/float/mod1_evaluator.go @@ -4,18 +4,17 @@ import ( "fmt" "math/big" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/rlwe" ) // EvaluatorForMod1 defines a set of common and scheme agnostic // methods that are necessary to instantiate a Mod1Evaluator. -// The default ckks.Evaluator is compliant to this interface. +// The default float.Evaluator is compliant to this interface. type EvaluatorForMod1 interface { he.Evaluator DropLevel(*rlwe.Ciphertext, int) - GetParameters() *ckks.Parameters + GetParameters() *Parameters } // Mod1Evaluator is an evaluator providing an API for homomorphic evaluations of scaled x mod 1. @@ -27,7 +26,7 @@ type Mod1Evaluator struct { } // NewMod1Evaluator instantiates a new Mod1Evaluator evaluator. -// The default ckks.Evaluator is compliant to the EvaluatorForMod1 interface. +// The default float.Evaluator is compliant to the EvaluatorForMod1 interface. // This method is allocation free. func NewMod1Evaluator(eval EvaluatorForMod1, evalPoly *PolynomialEvaluator, Mod1Parameters Mod1Parameters) *Mod1Evaluator { return &Mod1Evaluator{EvaluatorForMod1: eval, PolynomialEvaluator: evalPoly, Mod1Parameters: Mod1Parameters} diff --git a/he/float/mod1_parameters.go b/he/float/mod1_parameters.go index e646eeed..a69f185b 100644 --- a/he/float/mod1_parameters.go +++ b/he/float/mod1_parameters.go @@ -7,7 +7,6 @@ import ( "math/big" "math/bits" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float/cosine" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils" @@ -117,7 +116,7 @@ func (evp Mod1Parameters) QDiff() float64 { // 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) { +func NewMod1ParametersFromLiteral(params Parameters, evm Mod1ParametersLiteral) (Mod1Parameters, error) { var mod1InvPoly *bignum.Polynomial var mod1Poly bignum.Polynomial diff --git a/he/float/mod1_test.go b/he/float/mod1_test.go index 7f0e8ff0..f977aab5 100644 --- a/he/float/mod1_test.go +++ b/he/float/mod1_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he/float" "github.com/tuneinsight/lattigo/v4/ring" "github.com/tuneinsight/lattigo/v4/rlwe" @@ -21,7 +20,7 @@ func TestMod1(t *testing.T) { t.Skip("skipping homomorphic mod tests for GOARCH=wasm") } - ParametersLiteral := ckks.ParametersLiteral{ + ParametersLiteral := float.ParametersLiteral{ LogN: 10, LogQ: []int{55, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 53}, LogP: []int{61, 61, 61, 61, 61}, @@ -31,12 +30,12 @@ func TestMod1(t *testing.T) { testMod1Marhsalling(t) - var params ckks.Parameters - if params, err = ckks.NewParametersFromLiteral(ParametersLiteral); err != nil { + var params float.Parameters + if params, err = float.NewParametersFromLiteral(ParametersLiteral); err != nil { t.Fatal(err) } - for _, testSet := range []func(params ckks.Parameters, t *testing.T){ + for _, testSet := range []func(params float.Parameters, t *testing.T){ testMod1, } { testSet(params, t) @@ -68,14 +67,14 @@ func testMod1Marhsalling(t *testing.T) { }) } -func testMod1(params ckks.Parameters, t *testing.T) { +func testMod1(params float.Parameters, t *testing.T) { - kgen := ckks.NewKeyGenerator(params) + kgen := rlwe.NewKeyGenerator(params) sk := kgen.GenSecretKeyNew() - ecd := ckks.NewEncoder(params) - enc := ckks.NewEncryptor(params, sk) - dec := ckks.NewDecryptor(params, sk) - eval := ckks.NewEvaluator(params, rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk))) + ecd := float.NewEncoder(params) + enc := rlwe.NewEncryptor(params, sk) + dec := rlwe.NewDecryptor(params, sk) + eval := float.NewEvaluator(params, rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk))) t.Run("SineContinuousWithArcSine", func(t *testing.T) { @@ -91,7 +90,7 @@ func testMod1(params ckks.Parameters, t *testing.T) { values, ciphertext := evaluateMod1(evm, params, ecd, enc, eval, t) - ckks.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run("CosDiscrete", func(t *testing.T) { @@ -108,7 +107,7 @@ func testMod1(params ckks.Parameters, t *testing.T) { values, ciphertext := evaluateMod1(evm, params, ecd, enc, eval, t) - ckks.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) t.Run("CosContinuous", func(t *testing.T) { @@ -125,11 +124,11 @@ func testMod1(params ckks.Parameters, t *testing.T) { values, ciphertext := evaluateMod1(evm, params, ecd, enc, eval, t) - ckks.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) + float.VerifyTestVectors(params, ecd, dec, values, ciphertext, params.LogDefaultScale(), 0, *printPrecisionStats, t) }) } -func evaluateMod1(evm float.Mod1ParametersLiteral, params ckks.Parameters, ecd *ckks.Encoder, enc *rlwe.Encryptor, eval *ckks.Evaluator, t *testing.T) ([]float64, *rlwe.Ciphertext) { +func evaluateMod1(evm float.Mod1ParametersLiteral, params float.Parameters, ecd *float.Encoder, enc *rlwe.Encryptor, eval *float.Evaluator, t *testing.T) ([]float64, *rlwe.Ciphertext) { mod1Parameters, err := float.NewMod1ParametersFromLiteral(params, evm) require.NoError(t, err) @@ -176,7 +175,7 @@ func evaluateMod1(evm float.Mod1ParametersLiteral, params ckks.Parameters, ecd * return values, ciphertext } -func newTestVectorsMod1(params ckks.Parameters, encryptor *rlwe.Encryptor, encoder *ckks.Encoder, evm float.Mod1Parameters, t *testing.T) (values []float64, plaintext *rlwe.Plaintext, ciphertext *rlwe.Ciphertext) { +func newTestVectorsMod1(params float.Parameters, encryptor *rlwe.Encryptor, encoder *float.Encoder, evm float.Mod1Parameters, t *testing.T) (values []float64, plaintext *rlwe.Plaintext, ciphertext *rlwe.Ciphertext) { logSlots := params.LogMaxDimensions().Cols @@ -191,7 +190,7 @@ func newTestVectorsMod1(params ckks.Parameters, encryptor *rlwe.Encryptor, encod values[0] = K*Q + 0.5 - plaintext = ckks.NewPlaintext(params, params.MaxLevel()) + plaintext = float.NewPlaintext(params, params.MaxLevel()) encoder.Encode(values, plaintext) diff --git a/he/float/polynomial_evaluator.go b/he/float/polynomial_evaluator.go index 3ec0575c..1d9b07fc 100644 --- a/he/float/polynomial_evaluator.go +++ b/he/float/polynomial_evaluator.go @@ -3,7 +3,6 @@ package float import ( "fmt" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils/bignum" @@ -12,7 +11,7 @@ import ( // PolynomialEvaluator is a wrapper of the he.PolynomialEvaluator. // All fields of this struct are public, enabling custom instantiations. type PolynomialEvaluator struct { - Parameters ckks.Parameters + Parameters Parameters he.EvaluatorForPolynomial } @@ -25,9 +24,9 @@ func NewPowerBasis(ct *rlwe.Ciphertext, basis bignum.Basis) he.PowerBasis { } // NewPolynomialEvaluator instantiates a new PolynomialEvaluator from a circuit.Evaluator. -// The default *ckks.Evaluator is compliant to the circuit.Evaluator interface. +// The default *float.Evaluator is compliant to the circuit.Evaluator interface. // This method is allocation free. -func NewPolynomialEvaluator(params ckks.Parameters, eval he.Evaluator) *PolynomialEvaluator { +func NewPolynomialEvaluator(params Parameters, eval he.Evaluator) *PolynomialEvaluator { return &PolynomialEvaluator{ Parameters: params, EvaluatorForPolynomial: &defaultCircuitEvaluatorForPolynomial{Evaluator: eval}, diff --git a/he/float/polynomial_evaluator_sim.go b/he/float/polynomial_evaluator_sim.go index 5fa3f851..a911cca3 100644 --- a/he/float/polynomial_evaluator_sim.go +++ b/he/float/polynomial_evaluator_sim.go @@ -4,7 +4,6 @@ import ( "math/big" "math/bits" - "github.com/tuneinsight/lattigo/v4/ckks" "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/rlwe" "github.com/tuneinsight/lattigo/v4/utils" @@ -17,7 +16,7 @@ import ( // with dummy operands. // This struct implements the interface he.SimEvaluator. type simEvaluator struct { - params ckks.Parameters + params Parameters levelsConsumedPerRescaling int } diff --git a/he/float/test_parameters_test.go b/he/float/test_parameters_test.go index d963158d..ea5a5555 100644 --- a/he/float/test_parameters_test.go +++ b/he/float/test_parameters_test.go @@ -1,13 +1,13 @@ package float_test import ( - "github.com/tuneinsight/lattigo/v4/ckks" + "github.com/tuneinsight/lattigo/v4/he/float" ) var ( // testInsecurePrec45 are insecure parameters used for the sole purpose of fast testing. - testInsecurePrec45 = ckks.ParametersLiteral{ + testInsecurePrec45 = float.ParametersLiteral{ LogN: 10, LogQ: []int{55, 45, 45, 45, 45, 45, 45}, LogP: []int{60}, @@ -15,12 +15,12 @@ var ( } // testInsecurePrec90 are insecure parameters used for the sole purpose of fast testing. - testInsecurePrec90 = ckks.ParametersLiteral{ + testInsecurePrec90 = float.ParametersLiteral{ LogN: 10, LogQ: []int{55, 55, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45}, LogP: []int{60, 60}, LogDefaultScale: 90, } - testParametersLiteral = []ckks.ParametersLiteral{testInsecurePrec45, testInsecurePrec90} + testParametersLiteral = []float.ParametersLiteral{testInsecurePrec45, testInsecurePrec90} ) diff --git a/he/integer/integer.go b/he/integer/integer.go index 0ed63fe7..eafd62f9 100644 --- a/he/integer/integer.go +++ b/he/integer/integer.go @@ -1,4 +1,4 @@ -// Package integer implements Homomorphic Encryption for encrypted modular arithmetic with integers. +// Package integer implements Homomorphic Encryption for encrypted modular arithmetic over the integers. package integer import ( diff --git a/rgsw/encryptor.go b/rgsw/encryptor.go index 9e08fcb3..a52770d1 100644 --- a/rgsw/encryptor.go +++ b/rgsw/encryptor.go @@ -10,15 +10,13 @@ import ( // types in addition to ciphertexts types in the rlwe package. type Encryptor struct { *rlwe.Encryptor - - params rlwe.Parameters buffQP ringqp.Poly } // NewEncryptor creates a new Encryptor type. Note that only secret-key encryption is // supported at the moment. -func NewEncryptor(params rlwe.Parameters, key rlwe.EncryptionKey) *Encryptor { - return &Encryptor{rlwe.NewEncryptor(params, key), params, params.RingQP().NewPoly()} +func NewEncryptor(params rlwe.ParameterProvider, key rlwe.EncryptionKey) *Encryptor { + return &Encryptor{rlwe.NewEncryptor(params, key), params.GetRLWEParameters().RingQP().NewPoly()} } // Encrypt encrypts a plaintext pt into a ciphertext ct, which can be a rgsw.Ciphertext @@ -35,8 +33,10 @@ func (enc Encryptor) Encrypt(pt *rlwe.Plaintext, ct interface{}) (err error) { return } + params := enc.GetRLWEParameters() + levelQ := rgswCt.LevelQ() - ringQ := enc.params.RingQ().AtLevel(levelQ) + ringQ := params.RingQ().AtLevel(levelQ) if pt != nil { @@ -58,7 +58,7 @@ func (enc Encryptor) Encrypt(pt *rlwe.Plaintext, ct interface{}) (err error) { if err := rlwe.AddPolyTimesGadgetVectorToGadgetCiphertext( enc.buffQP.Q, []rlwe.GadgetCiphertext{rgswCt.Value[0], rgswCt.Value[1]}, - *enc.params.RingQP(), + *params.RingQP(), enc.buffQP.Q); err != nil { // Sanity check, this error should not happen. panic(err) @@ -103,5 +103,5 @@ func (enc Encryptor) EncryptZero(ct interface{}) (err error) { // shared with the receiver and the temporary buffers are reallocated. The receiver and the returned // Encryptors can be used concurrently. func (enc Encryptor) ShallowCopy() *Encryptor { - return &Encryptor{Encryptor: enc.Encryptor.ShallowCopy(), params: enc.params, buffQP: enc.params.RingQP().NewPoly()} + return &Encryptor{Encryptor: enc.Encryptor.ShallowCopy(), buffQP: enc.GetRLWEParameters().RingQP().NewPoly()} } diff --git a/rgsw/evaluator.go b/rgsw/evaluator.go index 8dee7d1f..314beb1a 100644 --- a/rgsw/evaluator.go +++ b/rgsw/evaluator.go @@ -11,27 +11,25 @@ import ( // Evaluator.ExternalProduct). type Evaluator struct { rlwe.Evaluator - - params rlwe.Parameters } // NewEvaluator creates a new Evaluator type supporting RGSW operations in addition // to rlwe.Evaluator operations. -func NewEvaluator(params rlwe.Parameters, evk rlwe.EvaluationKeySet) *Evaluator { - return &Evaluator{*rlwe.NewEvaluator(params, evk), params} +func NewEvaluator(params rlwe.ParameterProvider, evk rlwe.EvaluationKeySet) *Evaluator { + return &Evaluator{*rlwe.NewEvaluator(params, evk)} } // ShallowCopy creates a shallow copy of this Evaluator in which all the read-only data-structures are // shared with the receiver and the temporary buffers are reallocated. The receiver and the returned // Evaluators can be used concurrently. func (eval Evaluator) ShallowCopy() *Evaluator { - return &Evaluator{*eval.Evaluator.ShallowCopy(), eval.params} + return &Evaluator{*eval.Evaluator.ShallowCopy()} } // WithKey creates a shallow copy of the receiver Evaluator for which the new EvaluationKey is evaluationKey // and where the temporary buffers are shared. The receiver and the returned Evaluators cannot be used concurrently. func (eval Evaluator) WithKey(evk rlwe.EvaluationKeySet) *Evaluator { - return &Evaluator{*eval.Evaluator.WithKey(evk), eval.params} + return &Evaluator{*eval.Evaluator.WithKey(evk)} } // ExternalProduct computes RLWE x RGSW -> RLWE @@ -54,8 +52,10 @@ func (eval Evaluator) ExternalProduct(op0 *rlwe.Ciphertext, op1 *Ciphertext, opO if levelP < 1 { + params := eval.GetRLWEParameters() + // If log(Q) * (Q-1)**2 < 2^{64}-1 - if ringQ := eval.params.RingQ(); levelQ == 0 && levelP == -1 && (ringQ.SubRings[0].Modulus>>29) == 0 { + if ringQ := params.RingQ(); levelQ == 0 && levelP == -1 && (ringQ.SubRings[0].Modulus>>29) == 0 { eval.externalProduct32Bit(op0, op1, c0QP.Q, c1QP.Q) ringQ.AtLevel(0).IMForm(c0QP.Q, opOut.Value[0]) ringQ.AtLevel(0).IMForm(c1QP.Q, opOut.Value[1]) @@ -84,7 +84,8 @@ func (eval Evaluator) externalProduct32Bit(ct0 *rlwe.Ciphertext, rgsw *Ciphertex // rgsw = [(-as + P*w*m1 + e, a), (-bs + e, b + P*w*m1)] // ct = [-cs + m0 + e, c] // opOut = [, ] = [ct[0] * rgsw[0][0] + ct[1] * rgsw[0][1], ct[0] * rgsw[1][0] + ct[1] * rgsw[1][1]] - ringQ := eval.params.RingQ().AtLevel(0) + params := eval.GetRLWEParameters() + ringQ := params.RingQ().AtLevel(0) subRing := ringQ.SubRings[0] pw2 := rgsw.Value[0].BaseTwoDecomposition mask := uint64(((1 << pw2) - 1)) @@ -100,6 +101,7 @@ func (eval Evaluator) externalProduct32Bit(ct0 *rlwe.Ciphertext, rgsw *Ciphertex for i, el := range rgsw.Value { ringQ.INTT(ct0.Value[i], eval.BuffInvNTT) for j := range el.Value[0] { + // TODO: center values if mask = 0 ring.MaskVec(eval.BuffInvNTT.Coeffs[0], j*pw2, mask, cw) if j == 0 && i == 0 { subRing.NTTLazy(cw, cwNTT) @@ -122,7 +124,8 @@ func (eval Evaluator) externalProductInPlaceSinglePAndBitDecomp(ct0 *rlwe.Cipher levelQ := rgsw.LevelQ() levelP := rgsw.LevelP() - ringQP := eval.params.RingQP().AtLevel(levelQ, levelP) + params := eval.GetRLWEParameters() + ringQP := params.RingQP().AtLevel(levelQ, levelP) ringQ := ringQP.RingQ ringP := ringQP.RingP @@ -143,6 +146,7 @@ func (eval Evaluator) externalProductInPlaceSinglePAndBitDecomp(ct0 *rlwe.Cipher cwNTT := eval.BuffBitDecomp for i := 0; i < BaseRNSDecompositionVectorSize; i++ { for j := 0; j < BaseTwoDecompositionVectorSize[i]; j++ { + // TODO: center values if mask == 0 ring.MaskVec(eval.BuffInvNTT.Coeffs[i], j*pw2, mask, cw) if k == 0 && i == 0 && j == 0 { @@ -184,7 +188,8 @@ func (eval Evaluator) externalProductInPlaceSinglePAndBitDecomp(ct0 *rlwe.Cipher func (eval Evaluator) externalProductInPlaceMultipleP(levelQ, levelP int, ct0 *rlwe.Ciphertext, rgsw *Ciphertext, c0OutQ, c0OutP, c1OutQ, c1OutP ring.Poly) { var reduce int - ringQP := eval.params.RingQP().AtLevel(levelQ, levelP) + params := eval.GetRLWEParameters() + ringQP := params.RingQP().AtLevel(levelQ, levelP) ringQ := ringQP.RingQ ringP := ringQP.RingP @@ -193,10 +198,10 @@ func (eval Evaluator) externalProductInPlaceMultipleP(levelQ, levelP int, ct0 *r c0QP := ringqp.Poly{Q: c0OutQ, P: c0OutP} c1QP := ringqp.Poly{Q: c1OutQ, P: c1OutP} - BaseRNSDecompositionVectorSize := eval.params.BaseRNSDecompositionVectorSize(levelQ, levelP) + BaseRNSDecompositionVectorSize := params.BaseRNSDecompositionVectorSize(levelQ, levelP) - QiOverF := eval.params.QiOverflowMargin(levelQ) >> 1 - PiOverF := eval.params.PiOverflowMargin(levelP) >> 1 + QiOverF := params.QiOverflowMargin(levelQ) >> 1 + PiOverF := params.PiOverflowMargin(levelP) >> 1 var c2NTT, c2InvNTT ring.Poly diff --git a/rlwe/decryptor.go b/rlwe/decryptor.go index 433ba152..9447661c 100644 --- a/rlwe/decryptor.go +++ b/rlwe/decryptor.go @@ -32,6 +32,11 @@ func NewDecryptor(params ParameterProvider, sk *SecretKey) *Decryptor { } } +// GetRLWEParameters returns the underlying rlwe.Parameters. +func (d Decryptor) GetRLWEParameters() *Parameters { + return &d.params +} + // DecryptNew decrypts the Ciphertext and returns the result in a new Plaintext. // Output pt MetaData will match the input ct MetaData. func (d Decryptor) DecryptNew(ct *Ciphertext) (pt *Plaintext) { diff --git a/rlwe/encryptor.go b/rlwe/encryptor.go index db103b23..30db0e40 100644 --- a/rlwe/encryptor.go +++ b/rlwe/encryptor.go @@ -56,6 +56,11 @@ type Encryptor struct { uniformSampler ringqp.UniformSampler } +// GetRLWEParameters returns the underlying rlwe.Parameters. +func (d Encryptor) GetRLWEParameters() *Parameters { + return &d.params +} + func newEncryptor(params Parameters) *Encryptor { prng, err := sampling.NewPRNG() diff --git a/rlwe/rlwe_test.go b/rlwe/rlwe_test.go index 6f8fc29c..38369ed7 100644 --- a/rlwe/rlwe_test.go +++ b/rlwe/rlwe_test.go @@ -102,7 +102,7 @@ func testUserDefinedParameters(t *testing.T) { t.Run("Parameters/UnmarshalJSON", func(t *testing.T) { var err error - // checks that ckks.Parameters can be unmarshalled with log-moduli definition without error + // checks that Parameters can be unmarshalled with log-moduli definition without error dataWithLogModuli := []byte(`{"LogN":13,"LogQ":[50,50],"LogP":[60]}`) var paramsWithLogModuli Parameters err = json.Unmarshal(dataWithLogModuli, ¶msWithLogModuli) @@ -113,7 +113,7 @@ func testUserDefinedParameters(t *testing.T) { require.True(t, paramsWithLogModuli.Xe() == DefaultXe) // Omitting Xe should result in Default being used require.True(t, paramsWithLogModuli.Xs() == DefaultXs) // Omitting Xs should result in Default being used - // checks that ckks.Parameters can be unmarshalled with log-moduli definition with empty or omitted P without error + // checks that Parameters can be unmarshalled with log-moduli definition with empty or omitted P without error for _, dataWithLogModuliNoP := range [][]byte{ []byte(`{"LogN":13,"LogQ":[50,50],"LogP":[],"RingType": "ConjugateInvariant"}`), []byte(`{"LogN":13,"LogQ":[50,50],"RingType": "ConjugateInvariant"}`),