diff --git a/examples/he/hefloat/euler/main.go b/examples/he/hefloat/euler/main.go deleted file mode 100644 index d66bdcb7..00000000 --- a/examples/he/hefloat/euler/main.go +++ /dev/null @@ -1,234 +0,0 @@ -package main - -import ( - "fmt" - "math" - "math/cmplx" - "time" - - "github.com/tuneinsight/lattigo/v4/core/rlwe" - "github.com/tuneinsight/lattigo/v4/he" - "github.com/tuneinsight/lattigo/v4/he/hefloat" - "github.com/tuneinsight/lattigo/v4/utils/bignum" -) - -func example() { - - var start time.Time - var err error - - // Schemes parameters are created from scratch - params, err := hefloat.NewParametersFromLiteral( - hefloat.ParametersLiteral{ - LogN: 14, - LogQ: []int{55, 40, 40, 40, 40, 40, 40, 40}, - LogP: []int{45, 45}, - LogDefaultScale: 40, - }) - if err != nil { - panic(err) - } - - fmt.Println() - fmt.Println("=========================================") - fmt.Println(" INSTANTIATING SCHEME ") - fmt.Println("=========================================") - fmt.Println() - - start = time.Now() - - kgen := rlwe.NewKeyGenerator(params) - - sk := kgen.GenSecretKeyNew() - - encryptor := rlwe.NewEncryptor(params, sk) - decryptor := rlwe.NewDecryptor(params, sk) - encoder := hefloat.NewEncoder(params) - evk := rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk)) - evaluator := hefloat.NewEvaluator(params, evk) - - fmt.Printf("Done in %s \n", time.Since(start)) - - logSlots := params.LogMaxSlots() - slots := 1 << logSlots - - fmt.Println() - 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("=========================================") - fmt.Println(" PLAINTEXT CREATION ") - fmt.Println("=========================================") - fmt.Println() - - start = time.Now() - - r := float64(16) - - pi := 3.141592653589793 - - values := make([]complex128, slots) - for i := range values { - values[i] = complex(2*pi, 0) - } - - plaintext := hefloat.NewPlaintext(params, params.MaxLevel()) - plaintext.Scale = plaintext.Scale.Div(rlwe.NewScale(r)) - if err := encoder.Encode(values, plaintext); err != nil { - panic(err) - } - - fmt.Printf("Done in %s \n", time.Since(start)) - - fmt.Println() - fmt.Println("=========================================") - fmt.Println(" ENCRYPTION ") - fmt.Println("=========================================") - fmt.Println() - - start = time.Now() - - ciphertext, err := encryptor.EncryptNew(plaintext) - if err != nil { - panic(err) - } - - fmt.Printf("Done in %s \n", time.Since(start)) - - printDebug(params, ciphertext, values, decryptor, encoder) - - fmt.Println() - fmt.Println("===============================================") - fmt.Printf(" EVALUATION OF i*x on %d values\n", slots) - fmt.Println("===============================================") - fmt.Println() - - start = time.Now() - - if err := evaluator.Mul(ciphertext, 1i, ciphertext); err != nil { - panic(err) - } - - fmt.Printf("Done in %s \n", time.Since(start)) - - for i := range values { - values[i] *= 1i - } - - printDebug(params, ciphertext, values, decryptor, encoder) - - fmt.Println() - fmt.Println("===============================================") - fmt.Printf(" EVALUATION of x/r on %d values\n", slots) - fmt.Println("===============================================") - fmt.Println() - - start = time.Now() - - ciphertext.Scale = ciphertext.Scale.Mul(rlwe.NewScale(r)) - - fmt.Printf("Done in %s \n", time.Since(start)) - - for i := range values { - values[i] /= complex(r, 0) - } - - printDebug(params, ciphertext, values, decryptor, encoder) - - fmt.Println() - fmt.Println("===============================================") - fmt.Printf(" EVALUATION of e^x on %d values\n", slots) - fmt.Println("===============================================") - fmt.Println() - - start = time.Now() - - coeffs := []complex128{ - 1.0, - 1.0, - 1.0 / 2, - 1.0 / 6, - 1.0 / 24, - 1.0 / 120, - 1.0 / 720, - 1.0 / 5040, - } - - // We create a new polynomial, with the standard basis [1, x, x^2, ...], with no interval. - poly := bignum.NewPolynomial(bignum.Monomial, coeffs, nil) - - polyEval := hefloat.NewPolynomialEvaluator(params, evaluator) - - if ciphertext, err = polyEval.Evaluate(ciphertext, poly, ciphertext.Scale); err != nil { - panic(err) - } - - fmt.Printf("Done in %s \n", time.Since(start)) - - for i := range values { - values[i] = cmplx.Exp(values[i]) - } - - printDebug(params, ciphertext, values, decryptor, encoder) - - fmt.Println() - fmt.Println("===============================================") - fmt.Printf(" EVALUATION of x^r on %d values\n", slots) - fmt.Println("===============================================") - fmt.Println() - - start = time.Now() - - monomialBasis := he.NewPowerBasis(ciphertext, bignum.Monomial) - if err = monomialBasis.GenPower(int(r), false, evaluator); err != nil { - panic(err) - } - ciphertext = monomialBasis.Value[int(r)] - - fmt.Printf("Done in %s \n", time.Since(start)) - - for i := range values { - values[i] = cmplx.Pow(values[i], complex(r, 0)) - } - - printDebug(params, ciphertext, values, decryptor, encoder) - - fmt.Println() - fmt.Println("=========================================") - fmt.Println(" DECRYPTION & DECODING ") - fmt.Println("=========================================") - fmt.Println() - - start = time.Now() - - fmt.Printf("Done in %s \n", time.Since(start)) - - printDebug(params, ciphertext, values, decryptor, encoder) - -} - -func printDebug(params hefloat.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []complex128, decryptor *rlwe.Decryptor, encoder *hefloat.Encoder) (valuesTest []complex128) { - - valuesTest = make([]complex128, ciphertext.Slots()) - - if err := encoder.Decode(decryptor.DecryptNew(ciphertext), valuesTest); err != nil { - panic(err) - } - - fmt.Println() - fmt.Printf("Level: %d (logQ = %d)\n", ciphertext.Level(), params.LogQLvl(ciphertext.Level())) - fmt.Printf("Scale: 2^%f\n", math.Log2(ciphertext.Scale.Float64())) - 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]) - fmt.Println() - - precStats := hefloat.GetPrecisionStats(params, encoder, nil, valuesWant, valuesTest, 0, false) - - fmt.Println(precStats.String()) - - return -} - -func main() { - example() -} diff --git a/examples/he/hefloat/polynomial/main.go b/examples/he/hefloat/polynomial/main.go deleted file mode 100644 index 042fa128..00000000 --- a/examples/he/hefloat/polynomial/main.go +++ /dev/null @@ -1,183 +0,0 @@ -package main - -import ( - "fmt" - "math" - "math/big" - - "github.com/tuneinsight/lattigo/v4/core/rlwe" - "github.com/tuneinsight/lattigo/v4/he/hefloat" - "github.com/tuneinsight/lattigo/v4/utils/bignum" - "github.com/tuneinsight/lattigo/v4/utils/sampling" -) - -func chebyshevinterpolation() { - - var err error - - // This example packs random 8192 float64 values in the range [-8, 8] - // and approximates the function f = 1/(exp(-x) + 1) over the range [-8, 8] - // for the even slots and the function g = f * (1-f) over the range [-8, 8] - // for the odd slots. - // The result is then parsed and compared to the expected result. - - // Scheme params are taken directly from the proposed defaults - params, err := hefloat.NewParametersFromLiteral( - hefloat.ParametersLiteral{ - LogN: 14, - LogQ: []int{55, 40, 40, 40, 40, 40, 40, 40}, - LogP: []int{45, 45}, - LogDefaultScale: 40, - }) - if err != nil { - panic(err) - } - - encoder := hefloat.NewEncoder(params) - - // Keys - kgen := rlwe.NewKeyGenerator(params) - sk, pk := kgen.GenKeyPairNew() - - // Encryptor - encryptor := rlwe.NewEncryptor(params, pk) - - // Decryptor - decryptor := rlwe.NewDecryptor(params, sk) - - // Evaluator with relinearization key - evaluator := hefloat.NewEvaluator(params, rlwe.NewMemEvaluationKeySet(kgen.GenRelinearizationKeyNew(sk))) - - // Values to encrypt - slots := params.MaxSlots() - values := make([]float64, slots) - for i := range values { - values[i] = sampling.RandFloat64(-8, 8) - } - - 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() - fmt.Printf("Values : %6f %6f %6f %6f...\n", - round(values[0]), round(values[1]), round(values[2]), round(values[3])) - fmt.Println() - - // Plaintext creation and encoding process - plaintext := hefloat.NewPlaintext(params, params.MaxLevel()) - if err := encoder.Encode(values, plaintext); err != nil { - panic(err) - } - - // Encryption process - var ciphertext *rlwe.Ciphertext - ciphertext, err = encryptor.EncryptNew(plaintext) - if err != nil { - panic(err) - } - - a, b := -8.0, 8.0 - deg := 63 - - fmt.Printf("Evaluation of the function f(x) for even slots and g(x) for odd slots in the range [%0.2f, %0.2f] (degree of approximation: %d)\n", a, b, deg) - - // Evaluation process - // We approximate f(x) in the range [-8, 8] with a Chebyshev interpolant of 33 coefficients (degree 32). - - interval := bignum.Interval{ - Nodes: deg, - A: *new(big.Float).SetFloat64(a), - B: *new(big.Float).SetFloat64(b), - } - - approxF := bignum.ChebyshevApproximation(f, interval) - approxG := bignum.ChebyshevApproximation(g, interval) - - // Map storing which polynomial has to be applied to which slot. - mapping := make(map[int][]int) - - idxF := make([]int, slots>>1) - idxG := make([]int, slots>>1) - for i := 0; i < slots>>1; i++ { - idxF[i] = i * 2 // Index with all even slots - idxG[i] = i*2 + 1 // Index with all odd slots - } - - mapping[0] = idxF // Assigns index of all even slots to poly[0] = f(x) - mapping[1] = idxG // Assigns index of all odd slots to poly[1] = g(x) - - // Change of variable - if err := evaluator.Mul(ciphertext, 2/(b-a), ciphertext); err != nil { - panic(err) - } - - if err := evaluator.Add(ciphertext, (-a-b)/(b-a), ciphertext); err != nil { - panic(err) - } - - if err := evaluator.Rescale(ciphertext, ciphertext); err != nil { - panic(err) - } - - polyVec, err := hefloat.NewPolynomialVector([]bignum.Polynomial{approxF, approxG}, mapping) - if err != nil { - panic(err) - } - - polyEval := hefloat.NewPolynomialEvaluator(params, evaluator) - - // We evaluate the interpolated Chebyshev interpolant on the ciphertext - if ciphertext, err = polyEval.Evaluate(ciphertext, polyVec, ciphertext.Scale); err != nil { - panic(err) - } - - fmt.Println("Done... Consumed levels:", params.MaxLevel()-ciphertext.Level()) - - // Computation of the reference values - for i := 0; i < slots>>1; i++ { - values[i*2] = f(values[i*2]) - values[i*2+1] = g(values[i*2+1]) - } - - // Print results and comparison - printDebug(params, ciphertext, values, decryptor, encoder) - -} - -func f(x float64) float64 { - return 1 / (math.Exp(-x) + 1) -} - -func g(x float64) float64 { - return f(x) * (1 - f(x)) -} - -func round(x float64) float64 { - return math.Round(x*100000000) / 100000000 -} - -func printDebug(params hefloat.Parameters, ciphertext *rlwe.Ciphertext, valuesWant []float64, decryptor *rlwe.Decryptor, encoder *hefloat.Encoder) (valuesTest []float64) { - - valuesTest = make([]float64, 1< Chebyshev + if err := eval.Mul(ct, scalar, ct); err != nil { + panic(err) + } + + if err := eval.Add(ct, constant, ct); err != nil { + panic(err) + } + + if err := eval.Rescale(ct, ct); err != nil { + panic(err) + } + + // Evaluates the polynomial + if ct, err = polyEval.Evaluate(ct, poly, params.DefaultScale()); err != nil { + panic(err) + } + + // Allocates a vector for the reference values and + // evaluates the same circuit on the plaintext values + want := make([]float64, ct.Slots()) + for i := range want { + want[i], _ = poly.Evaluate(values[i])[0].Float64() + //want[i] = sigmoid(values[i]) + } + + // Decrypts and print the stats about the precision. + PrintPrecisionStats(params, ct, want, ecd, dec) +} + +// GetChebyshevPoly returns the Chebyshev polynomial approximation of f the +// in the interval [-K, K] for the given degree. +func GetChebyshevPoly(K float64, degree int, f64 func(x float64) (y float64)) bignum.Polynomial { + + FBig := func(x *big.Float) (y *big.Float) { + xF64, _ := x.Float64() + return new(big.Float).SetPrec(x.Prec()).SetFloat64(f64(xF64)) + } + + var prec uint = 128 + + interval := bignum.Interval{ + A: *bignum.NewFloat(-K, prec), + B: *bignum.NewFloat(K, prec), + Nodes: degree, + } + + // Returns the polynomial. + return bignum.ChebyshevApproximation(FBig, interval) +} + +// PrintPrecisionStats decrypts, decodes and prints the precision stats of a ciphertext. +func PrintPrecisionStats(params hefloat.Parameters, ct *rlwe.Ciphertext, want []float64, ecd *hefloat.Encoder, dec *rlwe.Decryptor) { + + var err error + + // Decrypts the vector of plaintext values + pt := dec.DecryptNew(ct) + + // Decodes the plaintext + have := make([]float64, ct.Slots()) + if err = ecd.Decode(pt, have); err != nil { + panic(err) + } + + // Pretty prints some values + fmt.Printf("Have: ") + for i := 0; i < 4; i++ { + fmt.Printf("%20.15f ", have[i]) + } + fmt.Printf("...\n") + + fmt.Printf("Want: ") + for i := 0; i < 4; i++ { + fmt.Printf("%20.15f ", want[i]) + } + fmt.Printf("...\n") + + // Pretty prints the precision stats + fmt.Println(hefloat.GetPrecisionStats(params, ecd, dec, have, want, 0, false).String()) +} diff --git a/examples/he/hefloat/polynomial/main_test.go b/examples/single_party/applications/reals_sigmoid_chebyshev/main_test.go similarity index 100% rename from examples/he/hefloat/polynomial/main_test.go rename to examples/single_party/applications/reals_sigmoid_chebyshev/main_test.go diff --git a/examples/single_party/applications/reals_sigmoid_minimax/main.go b/examples/single_party/applications/reals_sigmoid_minimax/main.go new file mode 100644 index 00000000..fd01837b --- /dev/null +++ b/examples/single_party/applications/reals_sigmoid_minimax/main.go @@ -0,0 +1,211 @@ +// Package main implements an example of smooth function approximation using minimax polynomial interpolation. +package main + +import ( + "fmt" + "math" + "math/big" + + "github.com/tuneinsight/lattigo/v4/core/rlwe" + "github.com/tuneinsight/lattigo/v4/he/hefloat" + "github.com/tuneinsight/lattigo/v4/ring" + "github.com/tuneinsight/lattigo/v4/utils/bignum" + "github.com/tuneinsight/lattigo/v4/utils/sampling" +) + +func main() { + var err error + var params hefloat.Parameters + + // 128-bit secure parameters enabling depth-7 circuits. + // LogN:14, LogQP: 431. + if params, err = hefloat.NewParametersFromLiteral( + hefloat.ParametersLiteral{ + LogN: 14, // log2(ring degree) + LogQ: []int{55, 45, 45, 45, 45, 45, 45, 45}, // log2(primes Q) (ciphertext modulus) + LogP: []int{61}, // log2(primes P) (auxiliary modulus) + LogDefaultScale: 45, // log2(scale) + RingType: ring.ConjugateInvariant, + }); err != nil { + panic(err) + } + + // Key Generator + kgen := rlwe.NewKeyGenerator(params) + + // Secret Key + sk := kgen.GenSecretKeyNew() + + // Encoder + ecd := hefloat.NewEncoder(params) + + // Encryptor + enc := rlwe.NewEncryptor(params, sk) + + // Decryptor + dec := rlwe.NewDecryptor(params, sk) + + // Relinearization Key + rlk := kgen.GenRelinearizationKeyNew(sk) + + // Evaluation Key Set with the Relinearization Key + evk := rlwe.NewMemEvaluationKeySet(rlk) + + // Evaluator + eval := hefloat.NewEvaluator(params, evk) + + // Samples values in [-K, K] + K := 25.0 + + // Allocates a plaintext at the max level. + pt := hefloat.NewPlaintext(params, params.MaxLevel()) + + // Vector of plaintext values + values := make([]float64, pt.Slots()) + + // Populates the vector of plaintext values + for i := range values { + values[i] = sampling.RandFloat64(-K, K) + } + + // Encodes the vector of plaintext values + if err = ecd.Encode(values, pt); err != nil { + panic(err) + } + + // Encrypts the vector of plaintext values + var ct *rlwe.Ciphertext + if ct, err = enc.EncryptNew(pt); err != nil { + panic(err) + } + + sigmoid := func(x float64) (y float64) { + return 1 / (math.Exp(-x) + 1) + } + + // Minimax approximation of the sigmoid in the domain [-K, K] of degree 63. + poly := hefloat.NewPolynomial(GetMinimaxPoly(K, 63, sigmoid)) + + // Instantiates the polynomial evaluator + polyEval := hefloat.NewPolynomialEvaluator(params, eval) + + // Retrieves the change of basis y = scalar * x + constant + scalar, constant := poly.ChangeOfBasis() + + // Performes the change of basis Standard -> Chebyshev + if err := eval.Mul(ct, scalar, ct); err != nil { + panic(err) + } + + if err := eval.Add(ct, constant, ct); err != nil { + panic(err) + } + + if err := eval.Rescale(ct, ct); err != nil { + panic(err) + } + + // Evaluates the polynomial + if ct, err = polyEval.Evaluate(ct, poly, params.DefaultScale()); err != nil { + panic(err) + } + + // Allocates a vector for the reference values and + // evaluates the same circuit on the plaintext values + want := make([]float64, ct.Slots()) + for i := range want { + want[i], _ = poly.Evaluate(values[i])[0].Float64() + //want[i] = sigmoid(values[i]) + } + + // Decrypts and print the stats about the precision. + PrintPrecisionStats(params, ct, want, ecd, dec) +} + +// GetMinimaxPoly returns the minimax polynomial approximation of f the +// in the interval [-K, K] for the given degree. +func GetMinimaxPoly(K float64, degree int, f64 func(x float64) (y float64)) bignum.Polynomial { + + FBig := func(x *big.Float) (y *big.Float) { + xF64, _ := x.Float64() + return new(big.Float).SetPrec(x.Prec()).SetFloat64(f64(xF64)) + } + + // Bit-precision of the arbitrary precision arithmetic used by the minimax solver + var prec uint = 160 + + // Minimax (Remez) approximation of sigmoid + r := bignum.NewRemez(bignum.RemezParameters{ + // Function to Approximate + Function: FBig, + + // Polynomial basis of the approximation + Basis: bignum.Chebyshev, + + // Approximation in [A, B] of degree Nodes. + Intervals: []bignum.Interval{ + { + A: *bignum.NewFloat(-K, prec), + B: *bignum.NewFloat(K, prec), + Nodes: degree, + }, + }, + + // Bit-precision of the solver + Prec: prec, + + // Scan step for root finding + ScanStep: bignum.NewFloat(1/16.0, prec), + // Optimizes the scan-step for root finding + OptimalScanStep: true, + }) + + // Max 10 iters, and normalized min/max error of 1e-15 + fmt.Printf("Minimax Approximation of Degree %d\n", degree) + r.Approximate(10, 1e-15) + fmt.Println() + + // Shoes the coeffs with 50 decimals of precision + fmt.Printf("Minimax Chebyshev Coefficients [%f, %f]\n", -K, K) + r.ShowCoeffs(16) + fmt.Println() + + // Shows the min and max error with 50 decimals of precision + fmt.Println("Minimax Error") + r.ShowError(16) + fmt.Println() + + // Returns the polynomial. + return bignum.NewPolynomial(bignum.Chebyshev, r.Coeffs, [2]float64{-K, K}) +} + +// PrintPrecisionStats decrypts, decodes and prints the precision stats of a ciphertext. +func PrintPrecisionStats(params hefloat.Parameters, ct *rlwe.Ciphertext, want []float64, ecd *hefloat.Encoder, dec *rlwe.Decryptor) { + + var err error + + // Decrypts the vector of plaintext values + pt := dec.DecryptNew(ct) + + // Decodes the plaintext + have := make([]float64, ct.Slots()) + if err = ecd.Decode(pt, have); err != nil { + panic(err) + } + + // Pretty prints some values + fmt.Printf("Have: ") + for i := 0; i < 4; i++ { + fmt.Printf("%20.15f ", have[i]) + } + fmt.Printf("...\n") + + fmt.Printf("Want: ") + for i := 0; i < 4; i++ { + fmt.Printf("%20.15f ", want[i]) + } + fmt.Printf("...\n") + + // Pretty prints the precision stats + fmt.Println(hefloat.GetPrecisionStats(params, ecd, dec, have, want, 0, false).String()) +} diff --git a/examples/he/hefloat/template/main_test.go b/examples/single_party/applications/reals_sigmoid_minimax/main_test.go similarity index 100% rename from examples/he/hefloat/template/main_test.go rename to examples/single_party/applications/reals_sigmoid_minimax/main_test.go diff --git a/examples/single_party/applications/reals_vectorized_polynomial_evaluation/main.go b/examples/single_party/applications/reals_vectorized_polynomial_evaluation/main.go new file mode 100644 index 00000000..ddd463f9 --- /dev/null +++ b/examples/single_party/applications/reals_vectorized_polynomial_evaluation/main.go @@ -0,0 +1,208 @@ +// Package main implements an example of vectorized polynomial evaluation. +package main + +import ( + "fmt" + "math" + "math/big" + + "github.com/tuneinsight/lattigo/v4/core/rlwe" + "github.com/tuneinsight/lattigo/v4/he/hefloat" + "github.com/tuneinsight/lattigo/v4/ring" + "github.com/tuneinsight/lattigo/v4/utils/bignum" + "github.com/tuneinsight/lattigo/v4/utils/sampling" +) + +func main() { + var err error + var params hefloat.Parameters + + // 128-bit secure parameters enabling depth-7 circuits. + // LogN:14, LogQP: 431. + if params, err = hefloat.NewParametersFromLiteral( + hefloat.ParametersLiteral{ + LogN: 14, // log2(ring degree) + LogQ: []int{55, 45, 45, 45, 45, 45, 45, 45}, // log2(primes Q) (ciphertext modulus) + LogP: []int{61}, // log2(primes P) (auxiliary modulus) + LogDefaultScale: 45, // log2(scale) + RingType: ring.ConjugateInvariant, + }); err != nil { + panic(err) + } + + // Key Generator + kgen := rlwe.NewKeyGenerator(params) + + // Secret Key + sk := kgen.GenSecretKeyNew() + + // Encoder + ecd := hefloat.NewEncoder(params) + + // Encryptor + enc := rlwe.NewEncryptor(params, sk) + + // Decryptor + dec := rlwe.NewDecryptor(params, sk) + + // Relinearization Key + rlk := kgen.GenRelinearizationKeyNew(sk) + + // Evaluation Key Set with the Relinearization Key + evk := rlwe.NewMemEvaluationKeySet(rlk) + + // Evaluator + eval := hefloat.NewEvaluator(params, evk) + + // Samples values in [-K, K] + K := 25.0 + + // Allocates a plaintext at the max level. + pt := hefloat.NewPlaintext(params, params.MaxLevel()) + + // Vector of plaintext values + values := make([]float64, pt.Slots()) + + // Populates the vector of plaintext values + for i := range values { + values[i] = sampling.RandFloat64(-K, K) + } + + // Encodes the vector of plaintext values + if err = ecd.Encode(values, pt); err != nil { + panic(err) + } + + // Encrypts the vector of plaintext values + var ct *rlwe.Ciphertext + if ct, err = enc.EncryptNew(pt); err != nil { + panic(err) + } + + // f(x) + sigmoid := func(x float64) (y float64) { + return 1 / (math.Exp(-x) + 1) + } + + // g0(x) = f'(x) * (f(x)-0) + g0 := func(x float64) (y float64) { + y = sigmoid(x) + return y * (1 - y) * (y - 0) + } + + // g1(x) = f'(x) * (f(x)-1) + g1 := func(x float64) (y float64) { + y = sigmoid(x) + return y * (1 - y) * (y - 1) + } + + // Defines on which slots g0(x) and g1(x) have to be evaluated + even := make([]int, ct.Slots()>>1) // List of all even slots + odd := make([]int, ct.Slots()>>1) // List of all odd slots + for i := 0; i < ct.Slots()>>1; i++ { + even[i] = 2 * i + odd[i] = 2*i + 1 + } + + mapping := map[int][]int{ + 0: even, // g0(x) is evaluated on all even slots + 1: odd, // g1(x) is evaluated on all odd slots + } + + // Vectorized Chebyhsev approximation of g0(x) and g1(x) in the domain [-K, K] of degree 63. + var polys hefloat.PolynomialVector + if polys, err = hefloat.NewPolynomialVector([]bignum.Polynomial{ + GetChebyshevPoly(K, 63, g0), + GetChebyshevPoly(K, 63, g1), + }, mapping); err != nil { + panic(err) + } + + // Instantiates the polynomial evaluator + polyEval := hefloat.NewPolynomialEvaluator(params, eval) + + // Retrieves the vectorized change of basis y = scalar * x + constant + scalar, constant := polys.ChangeOfBasis(ct.Slots()) + + // Performes the vectorized change of basis Standard -> Chebyshev + if err := eval.Mul(ct, scalar, ct); err != nil { + panic(err) + } + + if err := eval.Add(ct, constant, ct); err != nil { + panic(err) + } + + if err := eval.Rescale(ct, ct); err != nil { + panic(err) + } + + // Evaluates the vectorized polynomial + if ct, err = polyEval.Evaluate(ct, polys, params.DefaultScale()); err != nil { + panic(err) + } + + // Allocates a vector for the reference values + want := make([]float64, ct.Slots()) + for i := 0; i < ct.Slots()>>1; i++ { + want[2*i+0], _ = polys.Value[0].Evaluate(values[2*i+0])[0].Float64() + want[2*i+1], _ = polys.Value[1].Evaluate(values[2*i+1])[0].Float64() + //want[2*i+0] = sigmoidDerivLabel0(values[2*i+0]) + //want[2*i+1] = sigmoidDerivLabel1(values[2*i+1]) + } + + // Decrypts and print the stats about the precision. + PrintPrecisionStats(params, ct, want, ecd, dec) +} + +// GetChebyshevPoly returns the Chebyshev polynomial approximation of f the +// in the interval [-K, K] for the given degree. +func GetChebyshevPoly(K float64, degree int, f64 func(x float64) (y float64)) bignum.Polynomial { + + FBig := func(x *big.Float) (y *big.Float) { + xF64, _ := x.Float64() + return new(big.Float).SetPrec(x.Prec()).SetFloat64(f64(xF64)) + } + + var prec uint = 128 + + interval := bignum.Interval{ + A: *bignum.NewFloat(-K, prec), + B: *bignum.NewFloat(K, prec), + Nodes: degree, + } + + // Returns the polynomial. + return bignum.ChebyshevApproximation(FBig, interval) +} + +// PrintPrecisionStats decrypts, decodes and prints the precision stats of a ciphertext. +func PrintPrecisionStats(params hefloat.Parameters, ct *rlwe.Ciphertext, want []float64, ecd *hefloat.Encoder, dec *rlwe.Decryptor) { + + var err error + + // Decrypts the vector of plaintext values + pt := dec.DecryptNew(ct) + + // Decodes the plaintext + have := make([]float64, ct.Slots()) + if err = ecd.Decode(pt, have); err != nil { + panic(err) + } + + // Pretty prints some values + fmt.Printf("Have: ") + for i := 0; i < 4; i++ { + fmt.Printf("%20.15f ", have[i]) + } + fmt.Printf("...\n") + + fmt.Printf("Want: ") + for i := 0; i < 4; i++ { + fmt.Printf("%20.15f ", want[i]) + } + fmt.Printf("...\n") + + // Pretty prints the precision stats + fmt.Println(hefloat.GetPrecisionStats(params, ecd, dec, have, want, 0, false).String()) +} diff --git a/examples/he/hefloat/tutorial/main_test.go b/examples/single_party/applications/reals_vectorized_polynomial_evaluation/main_test.go similarity index 100% rename from examples/he/hefloat/tutorial/main_test.go rename to examples/single_party/applications/reals_vectorized_polynomial_evaluation/main_test.go diff --git a/examples/single_party/templates/int/main.go b/examples/single_party/templates/int/main.go new file mode 100644 index 00000000..163cba6a --- /dev/null +++ b/examples/single_party/templates/int/main.go @@ -0,0 +1,111 @@ +// Package main is a template encrypted modular arithmetic integers, with a set of example parameters, key generation, encoding, encryption, decryption and decoding. +package main + +import ( + "fmt" + "math/rand" + + "github.com/tuneinsight/lattigo/v4/core/rlwe" + "github.com/tuneinsight/lattigo/v4/he/heint" + "github.com/tuneinsight/lattigo/v4/utils" +) + +func main() { + var err error + var params heint.Parameters + + // 128-bit secure parameters enabling depth-7 circuits. + // LogN:14, LogQP: 431. + if params, err = heint.NewParametersFromLiteral( + heint.ParametersLiteral{ + LogN: 14, // log2(ring degree) + LogQ: []int{55, 45, 45, 45, 45, 45, 45, 45}, // log2(primes Q) (ciphertext modulus) + LogP: []int{61}, // log2(primes P) (auxiliary modulus) + PlaintextModulus: 0x10001, // log2(scale) + }); err != nil { + panic(err) + } + + // Key Generator + kgen := rlwe.NewKeyGenerator(params) + + // Secret Key + sk := kgen.GenSecretKeyNew() + + // Encoder + ecd := heint.NewEncoder(params) + + // Encryptor + enc := rlwe.NewEncryptor(params, sk) + + // Decryptor + dec := rlwe.NewDecryptor(params, sk) + + // Vector of plaintext values + values := make([]uint64, params.MaxSlots()) + + // Source for sampling random plaintext values (not cryptographically secure) + /* #nosec G404 */ + r := rand.New(rand.NewSource(0)) + + // Populates the vector of plaintext values + T := params.PlaintextModulus() + for i := range values { + values[i] = r.Uint64() % T + } + + // Allocates a plaintext at the max level. + // Default rlwe.MetaData: + // - IsBatched = true (slots encoding) + // - Scale = params.DefaultScale() + pt := heint.NewPlaintext(params, params.MaxLevel()) + + // Encodes the vector of plaintext values + if err = ecd.Encode(values, pt); err != nil { + panic(err) + } + + // Encrypts the vector of plaintext values + var ct *rlwe.Ciphertext + if ct, err = enc.EncryptNew(pt); err != nil { + panic(err) + } + + // Allocates a vector for the reference values + want := make([]uint64, params.MaxSlots()) + copy(want, values) + + PrintPrecisionStats(params, ct, want, ecd, dec) +} + +// PrintPrecisionStats decrypts, decodes and prints the precision stats of a ciphertext. +func PrintPrecisionStats(params heint.Parameters, ct *rlwe.Ciphertext, want []uint64, ecd *heint.Encoder, dec *rlwe.Decryptor) { + + var err error + + // Decrypts the vector of plaintext values + pt := dec.DecryptNew(ct) + + // Decodes the plaintext + have := make([]uint64, params.MaxSlots()) + if err = ecd.Decode(pt, have); err != nil { + panic(err) + } + + // Pretty prints some values + fmt.Printf("Have: ") + for i := 0; i < 4; i++ { + fmt.Printf("%d ", have[i]) + } + fmt.Printf("...\n") + + fmt.Printf("Want: ") + for i := 0; i < 4; i++ { + fmt.Printf("%d ", want[i]) + } + fmt.Printf("...\n") + + if !utils.EqualSlice(want, have) { + panic("wrong result: bad decryption or encrypted/plaintext circuits do not match") + } +} diff --git a/examples/mhe/thresh_eval_key_gen/main_test.go b/examples/single_party/templates/int/main_test.go similarity index 100% rename from examples/mhe/thresh_eval_key_gen/main_test.go rename to examples/single_party/templates/int/main_test.go diff --git a/examples/he/hefloat/template/main.go b/examples/single_party/templates/reals/main.go similarity index 99% rename from examples/he/hefloat/template/main.go rename to examples/single_party/templates/reals/main.go index a551e3be..46b888cd 100644 --- a/examples/he/hefloat/template/main.go +++ b/examples/single_party/templates/reals/main.go @@ -14,6 +14,7 @@ func main() { var params hefloat.Parameters // 128-bit secure parameters enabling depth-7 circuits. + // LogN:14, LogQP: 431. if params, err = hefloat.NewParametersFromLiteral( hefloat.ParametersLiteral{ LogN: 14, // log2(ring degree) diff --git a/examples/single_party/templates/reals/main_test.go b/examples/single_party/templates/reals/main_test.go new file mode 100644 index 00000000..6cbdcc76 --- /dev/null +++ b/examples/single_party/templates/reals/main_test.go @@ -0,0 +1,10 @@ +package main + +import "testing" + +func TestMain(t *testing.T) { + if testing.Short() { + t.Skip("skipped in -short mode") + } + main() +} diff --git a/examples/he/hefloat/tutorial/main.go b/examples/single_party/tutorials/reals/main.go similarity index 100% rename from examples/he/hefloat/tutorial/main.go rename to examples/single_party/tutorials/reals/main.go diff --git a/examples/single_party/tutorials/reals/main_test.go b/examples/single_party/tutorials/reals/main_test.go new file mode 100644 index 00000000..6cbdcc76 --- /dev/null +++ b/examples/single_party/tutorials/reals/main_test.go @@ -0,0 +1,10 @@ +package main + +import "testing" + +func TestMain(t *testing.T) { + if testing.Short() { + t.Skip("skipped in -short mode") + } + main() +} diff --git a/he/hefloat/polynomial.go b/he/hefloat/polynomial.go index 7fd27a7e..b29e39d1 100644 --- a/he/hefloat/polynomial.go +++ b/he/hefloat/polynomial.go @@ -1,6 +1,8 @@ package hefloat import ( + "math/big" + "github.com/tuneinsight/lattigo/v4/he" "github.com/tuneinsight/lattigo/v4/utils/bignum" ) @@ -29,3 +31,25 @@ func NewPolynomialVector(polys []bignum.Polynomial, mapping map[int][]int) (Poly p, err := he.NewPolynomialVector(polys, mapping) return PolynomialVector(p), err } + +func (p PolynomialVector) ChangeOfBasis(slots int) (scalar, constant []*big.Float) { + + scalar = make([]*big.Float, slots) + constant = make([]*big.Float, slots) + + for i := 0; i < slots; i++ { + scalar[i] = new(big.Float) + constant[i] = new(big.Float) + } + + for i := range p.Mapping { + m := p.Mapping[i] + s, c := p.Value[i].ChangeOfBasis() + for _, j := range m { + scalar[j] = s + constant[j] = c + } + } + + return +}