diff --git a/ckks/bootstrapp.go b/ckks/bootstrapp.go index c7ef5c56..e30a013e 100644 --- a/ckks/bootstrapp.go +++ b/ckks/bootstrapp.go @@ -502,7 +502,7 @@ func (btp *Bootstrapper) evaluateCheby(ct *Ciphertext) (res *Ciphertext) { eval.AddConst(C[1], -0.5/(scfac*(cheby.b-cheby.a)), C[1]) } - res = eval.evalCheby(cheby, C, btp.relinkey) + res, _ = eval.evalCheby(cheby, C, btp.relinkey) sqrt2pi := math.Pow(0.15915494309189535, 1.0/float64(int(1< cannot evaluate", levels, logDegree) + } + + return nil +} + // Degree returns the degree of the polynomial func (p *Poly) Degree() uint64 { return uint64(len(p.coeffs) - 1) @@ -61,7 +77,13 @@ func computeSmallPoly(split uint64, coeffs *Poly) (polyList []*Poly) { } // EvaluatePoly evaluates a polynomial in standard basis on the input Ciphertext in ceil(log2(deg+1)) levels. -func (eval *evaluator) EvaluatePoly(ct0 *Ciphertext, pol *Poly, evakey *EvaluationKey) (res *Ciphertext) { +// Returns an error if the input ciphertext does not have enough level to carry out the full polynomial evaluation. +// Returns an error if something is wrong with the scale. +func (eval *evaluator) EvaluatePoly(ct0 *Ciphertext, pol *Poly, evakey *EvaluationKey) (opOut *Ciphertext, err error) { + + if err := checkEnoughLevels(ct0.Level(), pol, 1); err != nil { + return ct0, err + } C := make(map[uint64]*Ciphertext) @@ -78,15 +100,19 @@ func (eval *evaluator) EvaluatePoly(ct0 *Ciphertext, pol *Poly, evakey *Evaluati computePowerBasis(1<> 1) //optimalSplit(logDegree) // for i := uint64(2); i < (1 << logSplit); i++ { - computePowerBasisCheby(i, C, eval, evakey) + if err = computePowerBasisCheby(i, C, eval, evakey); err != nil { + return nil, err + } } for i := logSplit; i < logDegree; i++ { - computePowerBasisCheby(1<> 1 // Recurses on the given indexes - computePowerBasis(a, C, evaluator, evakey) - computePowerBasis(b, C, evaluator, evakey) + if err = computePowerBasis(a, C, evaluator, evakey); err != nil { + return err + } + if err = computePowerBasis(b, C, evaluator, evakey); err != nil { + return err + } // Computes C[n] = C[a]*C[b] C[n] = evaluator.MulRelinNew(C[a], C[b], evakey) - evaluator.Rescale(C[n], evaluator.scale, C[n]) + if err = evaluator.Rescale(C[n], evaluator.scale, C[n]); err != nil { + return err + } } + + return nil } -func computePowerBasisCheby(n uint64, C map[uint64]*Ciphertext, evaluator *evaluator, evakey *EvaluationKey) { +func computePowerBasisCheby(n uint64, C map[uint64]*Ciphertext, evaluator *evaluator, evakey *EvaluationKey) (err error) { // Given a hash table with the first three evaluations of the Chebyshev ring at x in the interval a, b: // C0 = 1 (actually not stored in the hash table) @@ -171,18 +215,26 @@ func computePowerBasisCheby(n uint64, C map[uint64]*Ciphertext, evaluator *evalu c := uint64(math.Abs(float64(a) - float64(b))) // Recurses on the given indexes - computePowerBasisCheby(a, C, evaluator, evakey) - computePowerBasisCheby(b, C, evaluator, evakey) + if err = computePowerBasisCheby(a, C, evaluator, evakey); err != nil { + return err + } + if err = computePowerBasisCheby(b, C, evaluator, evakey); err != nil { + return err + } // Since C[0] is not stored (but rather seen as the constant 1), only recurses on c if c!= 0 if c != 0 { - computePowerBasisCheby(c, C, evaluator, evakey) + if err = computePowerBasisCheby(c, C, evaluator, evakey); err != nil { + return err + } } // Computes C[n] = C[a]*C[b] //fmt.Println("Mul", C[a].Level(), C[b].Level()) C[n] = evaluator.MulRelinNew(C[a], C[b], evakey) - evaluator.Rescale(C[n], evaluator.scale, C[n]) + if err = evaluator.Rescale(C[n], evaluator.scale, C[n]); err != nil { + return err + } // Computes C[n] = 2*C[a]*C[b] evaluator.Add(C[n], C[n], C[n]) @@ -195,6 +247,8 @@ func computePowerBasisCheby(n uint64, C map[uint64]*Ciphertext, evaluator *evalu } } + + return nil } func splitCoeffs(coeffs *Poly, split uint64) (coeffsq, coeffsr *Poly) { @@ -261,7 +315,7 @@ func splitCoeffsCheby(coeffs *Poly, split uint64) (coeffsq, coeffsr *Poly) { return coeffsq, coeffsr } -func recurse(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, C map[uint64]*Ciphertext, evaluator *evaluator, evakey *EvaluationKey) (res *Ciphertext) { +func recurse(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, C map[uint64]*Ciphertext, evaluator *evaluator, evakey *EvaluationKey) (res *Ciphertext, err error) { // Recursively computes the evalution of the Chebyshev polynomial using a baby-set giant-step algorithm. if coeffs.Degree() < (1 << logSplit) { @@ -295,10 +349,14 @@ func recurse(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, C ma //fmt.Printf("X^%2d: %f %f\n", nextPower, targetScale, targetScale* currentQi / C[nextPower].Scale()) //fmt.Printf("X^%2d : qi %d %t %d %d\n", nextPower, level, coeffsq.lead, coeffsq.maxDeg, 1<<(logDegree-1)) //fmt.Println() + var tmp *Ciphertext + if res, err = recurse(targetScale*currentQi/C[nextPower].Scale(), logSplit, logDegree, coeffsq, C, evaluator, evakey); err != nil { + return nil, err + } - res = recurse(targetScale*currentQi/C[nextPower].Scale(), logSplit, logDegree, coeffsq, C, evaluator, evakey) - - tmp := recurse(targetScale, logSplit, logDegree, coeffsr, C, evaluator, evakey) + if tmp, err = recurse(targetScale, logSplit, logDegree, coeffsr, C, evaluator, evakey); err != nil { + return nil, err + } if res.Level() > tmp.Level() { for res.Level() != tmp.Level()+1 { @@ -310,13 +368,18 @@ func recurse(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, C ma evaluator.MulRelin(res, C[nextPower], evakey, res) if res.Level() > tmp.Level() { - evaluator.Rescale(res, evaluator.scale, res) + if err = evaluator.Rescale(res, evaluator.scale, res); err != nil { + return nil, err + } //fmt.Printf("%f = %d) + (%d %f) = ", res.Scale(), res.Level(), tmp.Level(), tmp.Scale()) evaluator.Add(res, tmp, res) //fmt.Printf("(%d %f) %f\n", res.Level(), res.Scale(), res.Scale()-tmp.Scale()) } else { evaluator.Add(res, tmp, res) - evaluator.Rescale(res, evaluator.scale, res) + if err = evaluator.Rescale(res, evaluator.scale, res); err != nil { + return nil, err + } + } tmp = nil @@ -324,7 +387,7 @@ func recurse(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, C ma return } -func recurseCheby(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, C map[uint64]*Ciphertext, evaluator *evaluator, evakey *EvaluationKey) (res *Ciphertext) { +func recurseCheby(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, C map[uint64]*Ciphertext, evaluator *evaluator, evakey *EvaluationKey) (res *Ciphertext, err error) { // Recursively computes the evalution of the Chebyshev polynomial using a baby-set giant-step algorithm. if coeffs.Degree() < (1 << logSplit) { @@ -359,10 +422,14 @@ func recurseCheby(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, //fmt.Printf("X^%2d : qi %d %t %d %d\n", nextPower, level, coeffsq.lead, coeffsq.maxDeg, 1<<(logDegree-1)) //fmt.Println() - res = recurseCheby(targetScale*currentQi/C[nextPower].Scale(), logSplit, logDegree, coeffsq, C, evaluator, evakey) + if res, err = recurseCheby(targetScale*currentQi/C[nextPower].Scale(), logSplit, logDegree, coeffsq, C, evaluator, evakey); err != nil { + return nil, err + } var tmp *Ciphertext - tmp = recurseCheby(targetScale, logSplit, logDegree, coeffsr, C, evaluator, evakey) + if tmp, err = recurseCheby(targetScale, logSplit, logDegree, coeffsr, C, evaluator, evakey); err != nil { + return nil, err + } if res.Level() > tmp.Level() { for res.Level() != tmp.Level()+1 { @@ -374,13 +441,17 @@ func recurseCheby(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, evaluator.MulRelin(res, C[nextPower], evakey, res) if res.Level() > tmp.Level() { - evaluator.Rescale(res, evaluator.scale, res) + if err = evaluator.Rescale(res, evaluator.scale, res); err != nil { + return nil, err + } //fmt.Printf("%f = %d) + (%d %f) = ", res.Scale(), res.Level(), tmp.Level(), tmp.Scale()) evaluator.Add(res, tmp, res) //fmt.Printf("(%d %f) %f\n", res.Level(), res.Scale(), res.Scale()-tmp.Scale()) } else { evaluator.Add(res, tmp, res) - evaluator.Rescale(res, evaluator.scale, res) + if err = evaluator.Rescale(res, evaluator.scale, res); err != nil { + return nil, err + } } tmp = nil @@ -389,7 +460,7 @@ func recurseCheby(targetScale float64, logSplit, logDegree uint64, coeffs *Poly, } -func evaluatePolyFromPowerBasis(targetScale float64, coeffs *Poly, C map[uint64]*Ciphertext, evaluator *evaluator) (res *Ciphertext) { +func evaluatePolyFromPowerBasis(targetScale float64, coeffs *Poly, C map[uint64]*Ciphertext, evaluator *evaluator) (res *Ciphertext, err error) { if coeffs.Degree() == 0 { @@ -430,7 +501,9 @@ func evaluatePolyFromPowerBasis(targetScale float64, coeffs *Poly, C map[uint64] } } - evaluator.Rescale(res, evaluator.scale, res) + if err = evaluator.Rescale(res, evaluator.scale, res); err != nil { + return nil, err + } return } diff --git a/examples/ckks/euler/main.go b/examples/ckks/euler/main.go index a8a5dc95..f6201f32 100644 --- a/examples/ckks/euler/main.go +++ b/examples/ckks/euler/main.go @@ -153,7 +153,9 @@ func example() { poly := ckks.NewPoly(coeffs) - ciphertext = evaluator.EvaluatePoly(ciphertext, poly, rlk) + if ciphertext, err = evaluator.EvaluatePoly(ciphertext, poly, rlk); err != nil { + panic(err) + } fmt.Printf("Done in %s \n", time.Since(start)) diff --git a/examples/ckks/sigmoid/main.go b/examples/ckks/sigmoid/main.go index 138f6737..444e84a2 100644 --- a/examples/ckks/sigmoid/main.go +++ b/examples/ckks/sigmoid/main.go @@ -20,6 +20,8 @@ func randomComplex(min, max float64) complex128 { func chebyshevinterpolation() { + var err error + // This example packs random 8192 float64 values in the range [-8, 8] // and approximates the function 1/(exp(-x) + 1) over the range [-8, 8]. // The result is then parsed and compared to the expected result. @@ -79,7 +81,9 @@ func chebyshevinterpolation() { chebyapproximation := ckks.Approximate(f, -8, 8, 33) // We evaluate the interpolated Chebyshev interpolant on the ciphertext - ciphertext = evaluator.EvaluateCheby(ciphertext, chebyapproximation, rlk) + if ciphertext, err = evaluator.EvaluateCheby(ciphertext, chebyapproximation, rlk); err != nil { + panic(err) + } fmt.Println("Done... Consumed levels:", params.MaxLevel()-ciphertext.Level())