Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions circuits/ckks/bootstrapping/bootstrapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bootstrapping
import (
"flag"
"math"
"slices"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -15,6 +16,7 @@ import (
)

var flagLongTest = flag.Bool("long", false, "run the long test suite (all parameters + secure bootstrapping). Overrides -short and requires -timeout=0.")
var flagTestAllParams = flag.Bool("allparams", false, "test the bootstrapping with all default parameters. Can be combined with -long.")
var printPrecisionStats = flag.Bool("print-precision", false, "print precision stats")

var testPrec45 = ckks.ParametersLiteral{
Expand All @@ -24,6 +26,72 @@ var testPrec45 = ckks.ParametersLiteral{
LogDefaultScale: 40,
}

func TestAllParameters(t *testing.T) {
if !*flagTestAllParams {
t.Skip("this test is ran only with -allparams flag")
}

for _, paramSet := range slices.Concat(DefaultParametersDense, DefaultParametersSparse) {

residualParams := paramSet.SchemeParams
btpParamsLit := paramSet.BootstrappingParams

if !*flagLongTest {
residualParams.LogN = 12
btpParamsLit.LogN = utils.Pointy(residualParams.LogN)
}

params, err := ckks.NewParametersFromLiteral(residualParams)
require.Nil(t, err)

btpParams, err := NewParametersFromLiteral(params, btpParamsLit)
require.Nil(t, err)

sk := rlwe.NewKeyGenerator(btpParams.BootstrappingParameters).GenSecretKeyNew()

btpKeys, _, err := btpParams.GenEvaluationKeys(sk)
require.NoError(t, err)

evaluator, err := NewEvaluator(btpParams, btpKeys)
require.NoError(t, err)

ecd := ckks.NewEncoder(params)
enc := rlwe.NewEncryptor(params, sk)
dec := rlwe.NewDecryptor(params, sk)

values := make([]complex128, params.MaxSlots())
for i := range values {
values[i] = sampling.RandComplex128(-1, 1)
}

values[0] = complex(0.9238795325112867, 0.3826834323650898)
values[1] = complex(0.9238795325112867, 0.3826834323650898)
if len(values) > 2 {
values[2] = complex(0.9238795325112867, 0.3826834323650898)
values[3] = complex(0.9238795325112867, 0.3826834323650898)
}

plaintext := ckks.NewPlaintext(params, 0)
ecd.Encode(values, plaintext)

ctQ0, err := enc.EncryptNew(plaintext)
require.NoError(t, err)

// Checks that the input ciphertext is at the level 0
require.True(t, ctQ0.Level() == 0)

// Bootstrapps the ciphertext
ctQL, err := evaluator.Bootstrap(ctQ0)
require.NoError(t, err)

// Checks that the output ciphertext is at the max level of paramsN1
require.True(t, ctQL.Level() == params.MaxLevel())
require.True(t, ctQL.Scale.Equal(params.DefaultScale()))

verifyTestVectorsBootstrapping(params, ecd, dec, values, ctQL, t)
}
}

func TestBootstrapping(t *testing.T) {

t.Run("BootstrappingWithoutRingDegreeSwitch", func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions circuits/ckks/bootstrapping/default_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ var (
LogDefaultScale: 31,
},
ParametersLiteral{
LogN: utils.Pointy(15),
SlotsToCoeffsFactorizationDepthAndLogScales: [][]int{{30, 30}},
CoeffsToSlotsFactorizationDepthAndLogScales: [][]int{{52}, {52}},
EvalModLogScale: utils.Pointy(55),
Expand Down
35 changes: 26 additions & 9 deletions circuits/ckks/dft/dft.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func (eval *Evaluator) CoeffsToSlots(ctIn *rlwe.Ciphertext, ctsMatrices Matrix,

zV := ctIn.CopyNew()

if err = eval.dft(ctIn, ctsMatrices.Matrices, zV); err != nil {
if err = eval.dft(ctIn, ctsMatrices, zV); err != nil {
return fmt.Errorf("cannot CoeffsToSlots: %w", err)
}

Expand Down Expand Up @@ -289,7 +289,7 @@ func (eval *Evaluator) CoeffsToSlots(ctIn *rlwe.Ciphertext, ctsMatrices Matrix,
zV = nil

} else {
if err = eval.dft(ctIn, ctsMatrices.Matrices, ctReal); err != nil {
if err = eval.dft(ctIn, ctsMatrices, ctReal); err != nil {
return fmt.Errorf("cannot CoeffsToSlots: %w", err)
}
}
Expand Down Expand Up @@ -327,26 +327,43 @@ func (eval *Evaluator) SlotsToCoeffs(ctReal, ctImag *rlwe.Ciphertext, stcMatrice
return fmt.Errorf("cannot SlotsToCoeffs: %w", err)
}

if err = eval.dft(opOut, stcMatrices.Matrices, opOut); err != nil {
if err = eval.dft(opOut, stcMatrices, opOut); err != nil {
return fmt.Errorf("cannot SlotsToCoeffs: %w", err)
}
} else {
if err = eval.dft(ctReal, stcMatrices.Matrices, opOut); err != nil {
if err = eval.dft(ctReal, stcMatrices, opOut); err != nil {
return fmt.Errorf("cannot SlotsToCoeffs: %w", err)
}
}

return
}

// dft evaluates a series of [lintrans.LinearTransformation] sequentially on the ctIn and stores the result in opOut.
func (eval *Evaluator) dft(ctIn *rlwe.Ciphertext, matrices []ltcommon.LinearTransformation, opOut *rlwe.Ciphertext) (err error) {
// dft evaluates homorphically the iDFT/DFT [Matrix] on ctIn and stores the result in opOut.
func (eval *Evaluator) dft(ctIn *rlwe.Ciphertext, mat Matrix, opOut *rlwe.Ciphertext) (err error) {

inputLogSlots := ctIn.LogDimensions

// Sequentially multiplies w with the provided dft matrices.
if err = eval.LTEvaluator.EvaluateSequential(ctIn, matrices, opOut); err != nil {
return
matrixIdx := 0

for _, lvl := range mat.Levels {
for range lvl {
if matrixIdx == 0 {
if err = eval.LTEvaluator.Evaluate(ctIn, mat.Matrices[matrixIdx], opOut); err != nil {
return
}
} else {
if err = eval.LTEvaluator.Evaluate(opOut, mat.Matrices[matrixIdx], opOut); err != nil {
return
}
}

matrixIdx += 1

}
if err = eval.Rescale(opOut, opOut); err != nil {
return
}
}

// Encoding matrices are a special case of `fractal` linear transform
Expand Down