mirror of
https://github.com/luxfi/lattice.git
synced 2026-10-30 14:45:26 +00:00
- Update module path to github.com/luxfi/lattice/v7 - Add GPU acceleration with Metal (macOS) and CUDA backends - Enhance threshold FHE operations - Revert to vanilla Apache 2.0 license (remove tfhe) - Update Go version to 1.25.5 - Merge upstream lattigo improvements
90 lines
2.9 KiB
Go
90 lines
2.9 KiB
Go
package minimax
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/luxfi/lattice/v7/circuits/ckks/bootstrapping"
|
|
"github.com/luxfi/lattice/v7/circuits/ckks/polynomial"
|
|
"github.com/luxfi/lattice/v7/core/rlwe"
|
|
"github.com/luxfi/lattice/v7/ring"
|
|
"github.com/luxfi/lattice/v7/schemes/ckks"
|
|
)
|
|
|
|
// Evaluator is an evaluator used to evaluate composite polynomials on ciphertexts.
|
|
// All fields of this struct are publics, enabling custom instantiations.
|
|
type Evaluator struct {
|
|
*ckks.Evaluator
|
|
PolyEval *polynomial.Evaluator
|
|
BtsEval bootstrapping.Bootstrapper
|
|
Parameters ckks.Parameters
|
|
}
|
|
|
|
// NewEvaluator instantiates a new Evaluator.
|
|
// This method is allocation free.
|
|
func NewEvaluator(params ckks.Parameters, eval *ckks.Evaluator, btsEval bootstrapping.Bootstrapper) *Evaluator {
|
|
return &Evaluator{eval, polynomial.NewEvaluator(params, eval), btsEval, params}
|
|
}
|
|
|
|
// Evaluate evaluates the provided MinimaxCompositePolynomial on the input ciphertext.
|
|
func (eval Evaluator) Evaluate(ct *rlwe.Ciphertext, mcp Polynomial) (res *rlwe.Ciphertext, err error) {
|
|
|
|
params := eval.Parameters
|
|
|
|
btp := eval.BtsEval
|
|
|
|
levelsConsumedPerRescaling := params.LevelsConsumedPerRescaling()
|
|
|
|
// Checks that the number of levels available after the bootstrapping is enough to evaluate all polynomials
|
|
if maxDepth := mcp.MaxDepth() * levelsConsumedPerRescaling; params.MaxLevel() < maxDepth+btp.MinimumInputLevel() {
|
|
return nil, fmt.Errorf("parameters do not enable the evaluation of the minimax composite polynomial, required levels is %d but parameters only provide %d levels", maxDepth+btp.MinimumInputLevel(), params.MaxLevel())
|
|
}
|
|
|
|
res = ct.CopyNew()
|
|
|
|
for _, poly := range mcp {
|
|
|
|
// Checks that res has enough level to evaluate the next polynomial, else bootstrap
|
|
if res.Level() < poly.Depth()*params.LevelsConsumedPerRescaling()+btp.MinimumInputLevel() {
|
|
if res, err = btp.Bootstrap(res); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
// Define the scale that res must have after the polynomial evaluation.
|
|
// If we use the regular CKKS (with complex values), we chose a scale to be
|
|
// half of the desired scale, so that (x + conj(x)/2) has the correct scale.
|
|
var targetScale rlwe.Scale
|
|
if params.RingType() == ring.Standard {
|
|
targetScale = params.DefaultScale().Div(rlwe.NewScale(2))
|
|
} else {
|
|
targetScale = params.DefaultScale()
|
|
}
|
|
|
|
// Evaluate the polynomial
|
|
if res, err = eval.PolyEval.Evaluate(res, poly, targetScale); err != nil {
|
|
return nil, fmt.Errorf("evaluate polynomial: %w", err)
|
|
}
|
|
|
|
// Clean the imaginary part (else it tends to explode)
|
|
if params.RingType() == ring.Standard {
|
|
|
|
// Reassigns the scale back to the original one
|
|
res.Scale = res.Scale.Mul(rlwe.NewScale(2))
|
|
|
|
var resConj *rlwe.Ciphertext
|
|
if resConj, err = eval.ConjugateNew(res); err != nil {
|
|
return
|
|
}
|
|
|
|
if err = eval.Add(res, resConj, res); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Avoids float errors
|
|
res.Scale = ct.Scale
|
|
|
|
return
|
|
}
|