From 5ba0f09b580bb918326a56a24d4ac6d98632348a Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bossuat Date: Fri, 2 Oct 2020 12:33:02 +0200 Subject: [PATCH] [bfv/ckkd] : Moduli internals are now public --- bfv/bfv_test.go | 2 +- bfv/params.go | 375 ++++++++++++++------------ ckks/bootstrapp_params.go | 248 +++++++++-------- ckks/ckks_test.go | 5 +- ckks/params.go | 405 ++++++++++++++-------------- examples/ckks/bootstrapping/main.go | 4 +- examples/ckks/euler/main.go | 2 +- 7 files changed, 534 insertions(+), 507 deletions(-) diff --git a/bfv/bfv_test.go b/bfv/bfv_test.go index ce4ca795..eaf319c9 100644 --- a/bfv/bfv_test.go +++ b/bfv/bfv_test.go @@ -103,7 +103,7 @@ func genTestParams(params *Parameters) (testctx *testContext, err error) { func testParameters(testctx *testContext, t *testing.T) { t.Run("Parameters/NewParametersFromModuli", func(t *testing.T) { - p, err := NewParametersFromModuli(testctx.params.logN, testctx.params.Moduli, testctx.params.t) + p, err := NewParametersFromModuli(testctx.params.logN, testctx.params.Moduli(), testctx.params.t) assert.NoError(t, err) assert.True(t, p.Equals(testctx.params)) }) diff --git a/bfv/params.go b/bfv/params.go index 918cfe4f..ec9ed948 100644 --- a/bfv/params.go +++ b/bfv/params.go @@ -49,180 +49,68 @@ const DefaultSigma = 3.2 var DefaultParams = []*Parameters{ { - logN: 12, - t: 65537, - Moduli: Moduli{ - qi: []uint64{0x7ffffec001, 0x8000016001}, // 39 + 39 bits - pi: []uint64{0x40002001}, // 30 bits - qiMul: []uint64{0xfffffffffffc001, 0x100000000000e001}}, // 60 + 60 bits + logN: 12, + t: 65537, + qi: []uint64{0x7ffffec001, 0x8000016001}, // 39 + 39 bits + pi: []uint64{0x40002001}, // 30 bits + qiMul: []uint64{0xfffffffffffc001, 0x100000000000e001}, // 60 + 60 bits sigma: DefaultSigma, }, { - logN: 13, - t: 65537, - Moduli: Moduli{ - qi: []uint64{0x3fffffffef8001, 0x4000000011c001, 0x40000000120001}, // 54 + 54 + 54 bits - pi: []uint64{0x7ffffffffb4001}, // 55 bits - qiMul: []uint64{0xfffffffffffc001, 0xffffffffffe8001, 0x1000000000024001}}, // 60 + 60 + 60 bits + logN: 13, + t: 65537, + qi: []uint64{0x3fffffffef8001, 0x4000000011c001, 0x40000000120001}, // 54 + 54 + 54 bits + pi: []uint64{0x7ffffffffb4001}, // 55 bits + qiMul: []uint64{0xfffffffffffc001, 0xffffffffffe8001, 0x1000000000024001}, // 60 + 60 + 60 bits sigma: DefaultSigma, }, { logN: 14, t: 65537, - Moduli: Moduli{ - qi: []uint64{0x100000000060001, 0x80000000068001, 0x80000000080001, - 0x3fffffffef8001, 0x40000000120001, 0x3fffffffeb8001}, // 56 + 55 + 55 + 54 + 54 + 54 bits - pi: []uint64{0x80000000130001, 0x7fffffffe90001}, // 55 + 55 bits - qiMul: []uint64{0xffffffffffe8001, 0xffffffffffd8001, 0xffffffffffc0001, - 0x1000000000078001, 0xffffffffff28001, 0xfffffffffe38001}}, // 60 + 60 + 60 + 60 + 60 + 60 bits + qi: []uint64{0x100000000060001, 0x80000000068001, 0x80000000080001, + 0x3fffffffef8001, 0x40000000120001, 0x3fffffffeb8001}, // 56 + 55 + 55 + 54 + 54 + 54 bits + pi: []uint64{0x80000000130001, 0x7fffffffe90001}, // 55 + 55 bits + qiMul: []uint64{0xffffffffffe8001, 0xffffffffffd8001, 0xffffffffffc0001, + 0x1000000000078001, 0xffffffffff28001, 0xfffffffffe38001}, // 60 + 60 + 60 + 60 + 60 + 60 bits sigma: DefaultSigma, }, { logN: 15, t: 65537, - Moduli: Moduli{ - qi: []uint64{0x7ffffffffe70001, 0x7ffffffffe10001, 0x7ffffffffcc0001, // 59 + 59 + 59 bits - 0x400000000270001, 0x400000000350001, 0x400000000360001, // 58 + 58 + 58 bits - 0x3ffffffffc10001, 0x3ffffffffbe0001, 0x3ffffffffbd0001, // 58 + 58 + 58 bits - 0x4000000004d0001, 0x400000000570001, 0x400000000660001}, // 58 + 58 + 58 bits - pi: []uint64{0xffffffffffc0001, 0x10000000001d0001, 0x10000000006e0001}, // 60 + 60 + 60 bits - qiMul: []uint64{0xfffffffff840001, 0x1000000000860001, 0x1000000000870001, // 60 + 60 + 60 bits - 0x1000000000930001, 0xfffffffff6a0001, 0x1000000000980001, // 60 + 60 + 60 bits - 0xfffffffff5a0001, 0xfffffffff550001, 0x1000000000b00001, // 60 + 60 + 60 bits - 0xfffffffff330001, 0x1000000000ce0001, 0xfffffffff2a0001}}, // 60 + 60 + 60 bits + qi: []uint64{0x7ffffffffe70001, 0x7ffffffffe10001, 0x7ffffffffcc0001, // 59 + 59 + 59 bits + 0x400000000270001, 0x400000000350001, 0x400000000360001, // 58 + 58 + 58 bits + 0x3ffffffffc10001, 0x3ffffffffbe0001, 0x3ffffffffbd0001, // 58 + 58 + 58 bits + 0x4000000004d0001, 0x400000000570001, 0x400000000660001}, // 58 + 58 + 58 bits + pi: []uint64{0xffffffffffc0001, 0x10000000001d0001, 0x10000000006e0001}, // 60 + 60 + 60 bits + qiMul: []uint64{0xfffffffff840001, 0x1000000000860001, 0x1000000000870001, // 60 + 60 + 60 bits + 0x1000000000930001, 0xfffffffff6a0001, 0x1000000000980001, // 60 + 60 + 60 bits + 0xfffffffff5a0001, 0xfffffffff550001, 0x1000000000b00001, // 60 + 60 + 60 bits + 0xfffffffff330001, 0x1000000000ce0001, 0xfffffffff2a0001}, // 60 + 60 + 60 bits sigma: DefaultSigma, }, } // Moduli stores the NTT primes of the RNS representation. type Moduli struct { - qi []uint64 // Ciphertext prime moduli - pi []uint64 // Keys additional prime moduli - qiMul []uint64 // Ciphertext secondary prime moduli -} - -// Qi returns a new slice with the factors of the ciphertext modulus q -func (m *Moduli) Qi() []uint64 { - qi := make([]uint64, len(m.qi)) - copy(qi, m.qi) - return qi -} - -// QiCount returns the number of factors of the ciphertext modulus q -func (m *Moduli) QiCount() uint64 { - return uint64(len(m.qi)) -} - -// Pi returns a new slice with the factors of the ciphertext modulus extention P -func (m *Moduli) Pi() []uint64 { - pi := make([]uint64, len(m.pi)) - copy(pi, m.pi) - return pi -} - -// PiCount returns the number of factors of the ciphertext modulus extention P -func (m *Moduli) PiCount() uint64 { - return uint64(len(m.pi)) -} - -// QPiCount returns the number of factors of the ciphertext modulus Q + the modulus extension P -func (m *Moduli) QPiCount() uint64 { - return m.QiCount() + m.PiCount() -} - -// LogQP returns the size of the extended modulus QP in bits -func (m *Moduli) LogQP() uint64 { - tmp := ring.NewUint(1) - for _, qi := range m.qi { - tmp.Mul(tmp, ring.NewUint(qi)) - } - for _, pi := range m.pi { - tmp.Mul(tmp, ring.NewUint(pi)) - } - return uint64(tmp.BitLen()) -} - -// LogQ returns the size of the modulus Q in bits -func (m *Moduli) LogQ() uint64 { - tmp := ring.NewUint(1) - for _, qi := range m.qi { - tmp.Mul(tmp, ring.NewUint(qi)) - } - return uint64(tmp.BitLen()) -} - -// LogP returns the size of the modulus P in bits -func (m *Moduli) LogP() uint64 { - tmp := ring.NewUint(1) - for _, pi := range m.pi { - tmp.Mul(tmp, ring.NewUint(pi)) - } - return uint64(tmp.BitLen()) -} - -// LogQAlpha returns the size in bits of the sum of the norm of -// each element of the special RNS decomposition basis for the -// key-switching. -// LogQAlpha is the size of the element that is multipled by the -// error during the keyswitching and then divided by P. -// LogQAlpha should be smaller than P or the error added during -// the key-switching wont be negligible. -func (m *Moduli) LogQAlpha() uint64 { - - alpha := m.PiCount() - - if alpha == 0 { - return 0 - } - - res := ring.NewUint(0) - var j uint64 - for i := uint64(0); i < m.QiCount(); i = i + alpha { - - j = i + alpha - if j > m.QiCount() { - j = m.QiCount() - } - - tmp := ring.NewUint(1) - for _, qi := range m.qi[i:j] { - tmp.Mul(tmp, ring.NewUint(qi)) - } - - res.Add(res, tmp) - } - - return uint64(res.BitLen()) -} - -// Alpha returns the number of moduli in in P -func (m *Moduli) Alpha() uint64 { - return m.PiCount() -} - -// Beta returns the number of element in the RNS decomposition basis: Ceil(lenQi / lenPi) -func (m *Moduli) Beta() uint64 { - if m.Alpha() != 0 { - return uint64(math.Ceil(float64(m.QiCount()) / float64(m.Alpha()))) - } - - return 0 + Qi []uint64 // Ciphertext prime moduli + Pi []uint64 // Keys additional prime moduli + QiMul []uint64 // Ciphertext secondary prime moduli } // Copy creates a copy of the target Moduli. func (m *Moduli) Copy() Moduli { - qi := make([]uint64, len(m.qi)) - copy(qi, m.qi) + qi := make([]uint64, len(m.Qi)) + copy(qi, m.Qi) - pi := make([]uint64, len(m.pi)) - copy(pi, m.pi) + pi := make([]uint64, len(m.Pi)) + copy(pi, m.Pi) - qiMul := make([]uint64, len(m.qiMul)) - copy(qiMul, m.qiMul) + qiMul := make([]uint64, len(m.QiMul)) + copy(qiMul, m.QiMul) return Moduli{qi, pi, qiMul} } @@ -251,14 +139,16 @@ func (m *LogModuli) Copy() LogModuli { // Parameters represents a given parameter set for the BFV cryptosystem. type Parameters struct { - Moduli - logN uint64 // Log Ring degree (power of 2) + logN uint64 // Log Ring degree (power of 2) + qi []uint64 + pi []uint64 + qiMul []uint64 t uint64 // Plaintext modulus sigma float64 // Gaussian sampling standard deviation } // NewParametersFromModuli creates a new Parameters struct and returns a pointer to it. -func NewParametersFromModuli(logN uint64, m Moduli, t uint64) (p *Parameters, err error) { +func NewParametersFromModuli(logN uint64, m *Moduli, t uint64) (p *Parameters, err error) { p = new(Parameters) @@ -273,7 +163,14 @@ func NewParametersFromModuli(logN uint64, m Moduli, t uint64) (p *Parameters, er return nil, err } - p.Moduli = m.Copy() + p.qi = make([]uint64, len(m.Qi)) + copy(p.qi, m.Qi) + + p.pi = make([]uint64, len(m.Pi)) + copy(p.pi, m.Pi) + + p.qiMul = make([]uint64, len(m.QiMul)) + copy(p.qiMul, m.QiMul) p.sigma = DefaultSigma @@ -283,7 +180,7 @@ func NewParametersFromModuli(logN uint64, m Moduli, t uint64) (p *Parameters, er } // NewParametersFromLogModuli creates a new Parameters struct and returns a pointer to it. -func NewParametersFromLogModuli(logN uint64, lm LogModuli, t uint64) (p *Parameters, err error) { +func NewParametersFromLogModuli(logN uint64, lm *LogModuli, t uint64) (p *Parameters, err error) { if err = checkLogModuli(lm); err != nil { return nil, err @@ -326,23 +223,141 @@ func (p *Parameters) WithT(T uint64) (pCopy *Parameters) { } // LogModuli generates a LogModuli struct from the parameters' Moduli struct and returns it. -func (p *Parameters) LogModuli() LogModuli { - var lm LogModuli +func (p *Parameters) LogModuli() (lm *LogModuli) { + lm = new(LogModuli) lm.LogQi = make([]uint64, len(p.qi), len(p.qi)) for i := range p.qi { lm.LogQi[i] = uint64(math.Round(math.Log2(float64(p.qi[i])))) } - lm.LogPi = make([]uint64, len(p.pi), len(p.pi)) for i := range p.pi { lm.LogPi[i] = uint64(math.Round(math.Log2(float64(p.pi[i])))) } - lm.LogQiMul = make([]uint64, len(p.qiMul), len(p.qiMul)) for i := range p.qiMul { lm.LogQiMul[i] = uint64(math.Round(math.Log2(float64(p.qiMul[i])))) } - return lm + return +} + +// Moduli returns a struct Moduli with the moduli of the parameters +func (p *Parameters) Moduli() (m *Moduli) { + m = new(Moduli) + m.Qi = make([]uint64, len(p.qi)) + copy(m.Qi, p.qi) + m.Pi = make([]uint64, len(p.pi)) + copy(m.Pi, p.pi) + m.QiMul = make([]uint64, len(p.qiMul)) + copy(m.QiMul, p.qiMul) + return +} + +// Qi returns a new slice with the factors of the ciphertext modulus q +func (p *Parameters) Qi() []uint64 { + qi := make([]uint64, len(p.qi)) + copy(qi, p.qi) + return qi +} + +// QiCount returns the number of factors of the ciphertext modulus q +func (p *Parameters) QiCount() uint64 { + return uint64(len(p.qi)) +} + +// Pi returns a new slice with the factors of the ciphertext modulus extention P +func (p *Parameters) Pi() []uint64 { + pi := make([]uint64, len(p.pi)) + copy(pi, p.pi) + return pi +} + +// PiCount returns the number of factors of the ciphertext modulus extention P +func (p *Parameters) PiCount() uint64 { + return uint64(len(p.pi)) +} + +// QPiCount returns the number of factors of the ciphertext modulus Q + the modulus extension P +func (p *Parameters) QPiCount() uint64 { + return p.QiCount() + p.PiCount() +} + +// LogQP returns the size of the extended modulus QP in bits +func (p *Parameters) LogQP() uint64 { + tmp := ring.NewUint(1) + for _, qi := range p.qi { + tmp.Mul(tmp, ring.NewUint(qi)) + } + for _, pi := range p.pi { + tmp.Mul(tmp, ring.NewUint(pi)) + } + return uint64(tmp.BitLen()) +} + +// LogQ returns the size of the modulus Q in bits +func (p *Parameters) LogQ() uint64 { + tmp := ring.NewUint(1) + for _, qi := range p.qi { + tmp.Mul(tmp, ring.NewUint(qi)) + } + return uint64(tmp.BitLen()) +} + +// LogP returns the size of the modulus P in bits +func (p *Parameters) LogP() uint64 { + tmp := ring.NewUint(1) + for _, pi := range p.pi { + tmp.Mul(tmp, ring.NewUint(pi)) + } + return uint64(tmp.BitLen()) +} + +// LogQAlpha returns the size in bits of the sum of the norm of +// each element of the special RNS decomposition basis for the +// key-switching. +// LogQAlpha is the size of the element that is multipled by the +// error during the keyswitching and then divided by P. +// LogQAlpha should be smaller than P or the error added during +// the key-switching wont be negligible. +func (p *Parameters) LogQAlpha() uint64 { + + alpha := p.PiCount() + + if alpha == 0 { + return 0 + } + + res := ring.NewUint(0) + var j uint64 + for i := uint64(0); i < p.QiCount(); i = i + alpha { + + j = i + alpha + if j > p.QiCount() { + j = p.QiCount() + } + + tmp := ring.NewUint(1) + for _, qi := range p.qi[i:j] { + tmp.Mul(tmp, ring.NewUint(qi)) + } + + res.Add(res, tmp) + } + + return uint64(res.BitLen()) +} + +// Alpha returns the number of moduli in in P +func (p *Parameters) Alpha() uint64 { + return p.PiCount() +} + +// Beta returns the number of element in the RNS decomposition basis: Ceil(lenQi / lenPi) +func (p *Parameters) Beta() uint64 { + if p.Alpha() != 0 { + return uint64(math.Ceil(float64(p.QiCount()) / float64(p.Alpha()))) + } + + return 0 } // NewPolyQ returns a new empty polynomial of degree 2^logN in basis qi. @@ -367,8 +382,12 @@ func (p *Parameters) Copy() (paramsCopy *Parameters) { paramsCopy.logN = p.logN paramsCopy.t = p.t paramsCopy.sigma = p.sigma - paramsCopy.Moduli = p.Moduli.Copy() - + paramsCopy.qi = make([]uint64, len(p.qi)) + copy(paramsCopy.qi, p.qi) + paramsCopy.pi = make([]uint64, len(p.pi)) + copy(paramsCopy.pi, p.pi) + paramsCopy.qiMul = make([]uint64, len(p.qiMul)) + copy(paramsCopy.qiMul, p.qiMul) return } @@ -438,40 +457,40 @@ func (p *Parameters) UnmarshalBinary(data []byte) error { b.ReadUint64Slice(p.pi) b.ReadUint64Slice(p.qiMul) - err := checkModuli(p.Moduli, p.logN) // TODO: check more than moduli. + err := checkModuli(p.Moduli(), p.logN) // TODO: check more than moduli. if err != nil { return err } return nil } -func checkModuli(m Moduli, logN uint64) (err error) { +func checkModuli(m *Moduli, logN uint64) (err error) { - if len(m.qi) > MaxModuliCount { + if len(m.Qi) > MaxModuliCount { return fmt.Errorf("#qi is larger than %d", MaxModuliCount) } - if len(m.pi) > MaxModuliCount { + if len(m.Pi) > MaxModuliCount { return fmt.Errorf("#Pi is larger than %d", MaxModuliCount) } - if len(m.qiMul) > MaxModuliCount { + if len(m.QiMul) > MaxModuliCount { return fmt.Errorf("#qiMul is larger than %d", MaxModuliCount) } - for i, qi := range m.qi { + for i, qi := range m.Qi { if uint64(bits.Len64(qi)-1) > MaxModuliSize+1 { return fmt.Errorf("qi bit-size for i=%d is larger than %d", i, MaxModuliSize) } } - for i, pi := range m.pi { + for i, pi := range m.Pi { if uint64(bits.Len64(pi)-1) > MaxModuliSize+1 { return fmt.Errorf("Pi bit-size for i=%d is larger than %d", i, MaxModuliSize) } } - for i, qi := range m.qiMul { + for i, qi := range m.QiMul { if uint64(bits.Len64(qi)-1) > MaxModuliSize+1 { return fmt.Errorf("qiMul bitsize n°%d is larger than %d", i, MaxModuliSize) } @@ -479,19 +498,19 @@ func checkModuli(m Moduli, logN uint64) (err error) { N := uint64(1 << logN) - for i, qi := range m.qi { + for i, qi := range m.Qi { if !ring.IsPrime(qi) || qi&((N<<1)-1) != 1 { return fmt.Errorf("qi n°%d is not an NTT prime", i) } } - for i, pi := range m.pi { + for i, pi := range m.Pi { if !ring.IsPrime(pi) || pi&((N<<1)-1) != 1 { return fmt.Errorf("Pi n°%d is not an NTT prime", i) } } - for i, qi := range m.qiMul { + for i, qi := range m.QiMul { if !ring.IsPrime(qi) || qi&((N<<1)-1) != 1 { return fmt.Errorf("qiMul n°%d is not an NTT prime", i) } @@ -500,7 +519,7 @@ func checkModuli(m Moduli, logN uint64) (err error) { return nil } -func checkLogModuli(lm LogModuli) (err error) { +func checkLogModuli(lm *LogModuli) (err error) { // Checks if the parameters are empty if lm.LogQi == nil || len(lm.LogQi) == 0 { @@ -541,7 +560,9 @@ func checkLogModuli(lm LogModuli) (err error) { } // GenModuli generates the appropriate primes from the parameters using generateNTTPrimes such that all primes are different. -func genModuli(lm LogModuli, logN uint64) (m Moduli) { +func genModuli(lm *LogModuli, logN uint64) (m *Moduli) { + + m = new(Moduli) // Extracts all the different primes bit-size and maps their number primesbitlen := make(map[uint64]uint64) @@ -565,24 +586,24 @@ func genModuli(lm LogModuli, logN uint64) (m Moduli) { } // Assigns the primes to the CKKS moduli chain - m.qi = make([]uint64, len(lm.LogQi)) + m.Qi = make([]uint64, len(lm.LogQi)) for i, qi := range lm.LogQi { - m.qi[i] = primes[qi][0] + m.Qi[i] = primes[qi][0] primes[qi] = primes[qi][1:] } // Assigns the primes to the special primes list for the extended ring - m.pi = make([]uint64, len(lm.LogPi)) + m.Pi = make([]uint64, len(lm.LogPi)) for i, pj := range lm.LogPi { - m.pi[i] = primes[pj][0] + m.Pi[i] = primes[pj][0] primes[pj] = primes[pj][1:] } - m.qiMul = make([]uint64, len(lm.LogQiMul)) + m.QiMul = make([]uint64, len(lm.LogQiMul)) for i, qi := range lm.LogQiMul { - m.qiMul[i] = primes[qi][0] + m.QiMul[i] = primes[qi][0] primes[qi] = primes[qi][1:] } - return m + return } diff --git a/ckks/bootstrapp_params.go b/ckks/bootstrapp_params.go index 371184be..147c40e1 100644 --- a/ckks/bootstrapp_params.go +++ b/ckks/bootstrapp_params.go @@ -56,41 +56,39 @@ var DefaultBootstrappSchemeParams = []*Parameters{ { logN: 16, logSlots: 15, - Moduli: Moduli{ - qi: []uint64{ - 0x80000000080001, // 55 Q0 - 0x2000000a0001, // 45 - 0x2000000e0001, // 45 - 0x1fffffc20001, // 45 - 0x200000440001, // 45 - 0x200000500001, // 45 - 0x200000620001, // 45 - 0x1fffff980001, // 45 - 0x2000006a0001, // 45 - 0x1fffff7e0001, // 45 - 0x200000860001, // 45 - 0x100000000060001, // 56 StC (28 + 28) - 0xffa0001, // 28 StC - 0x80000000440001, // 55 Sine (double angle) - 0x7fffffffba0001, // 55 Sine (double angle) - 0x80000000500001, // 55 Sine - 0x7fffffffaa0001, // 55 Sine - 0x800000005e0001, // 55 Sine - 0x7fffffff7e0001, // 55 Sine - 0x7fffffff380001, // 55 Sine - 0x80000000ca0001, // 55 Sine - 0x200000000e0001, // 53 CtS - 0x20000000140001, // 53 CtS - 0x20000000280001, // 53 CtS - 0x1fffffffd80001, // 53 CtS - }, - pi: []uint64{ - 0xfffffffff00001, // 56 - 0xffffffffd80001, // 56 - 0x1000000002a0001, // 56 - 0xffffffffd20001, // 56 - 0x100000000480001, // 56 - }, + qi: []uint64{ + 0x80000000080001, // 55 Q0 + 0x2000000a0001, // 45 + 0x2000000e0001, // 45 + 0x1fffffc20001, // 45 + 0x200000440001, // 45 + 0x200000500001, // 45 + 0x200000620001, // 45 + 0x1fffff980001, // 45 + 0x2000006a0001, // 45 + 0x1fffff7e0001, // 45 + 0x200000860001, // 45 + 0x100000000060001, // 56 StC (28 + 28) + 0xffa0001, // 28 StC + 0x80000000440001, // 55 Sine (double angle) + 0x7fffffffba0001, // 55 Sine (double angle) + 0x80000000500001, // 55 Sine + 0x7fffffffaa0001, // 55 Sine + 0x800000005e0001, // 55 Sine + 0x7fffffff7e0001, // 55 Sine + 0x7fffffff380001, // 55 Sine + 0x80000000ca0001, // 55 Sine + 0x200000000e0001, // 53 CtS + 0x20000000140001, // 53 CtS + 0x20000000280001, // 53 CtS + 0x1fffffffd80001, // 53 CtS + }, + pi: []uint64{ + 0xfffffffff00001, // 56 + 0xffffffffd80001, // 56 + 0x1000000002a0001, // 56 + 0xffffffffd20001, // 56 + 0x100000000480001, // 56 }, scale: 1 << 45, sigma: DefaultSigma, @@ -99,38 +97,36 @@ var DefaultBootstrappSchemeParams = []*Parameters{ { logN: 16, logSlots: 15, - Moduli: Moduli{ - qi: []uint64{ - 0x80000000080001, // 55 Q0 - 0xffffffffffc0001, // 60 - 0x10000000006e0001, // 60 - 0xfffffffff840001, // 60 - 0x1000000000860001, // 60 - 0xfffffffff6a0001, // 60 - 0x1000000000980001, // 60 - 0xfffffffff5a0001, // 60 - 0x1000000000b00001, // 60 StC (30) - 0x1000000000ce0001, // 60 StC (30+30) - 0x80000000440001, // 55 Sine (double angle) - 0x7fffffffba0001, // 55 Sine (double angle) - 0x80000000500001, // 55 Sine - 0x7fffffffaa0001, // 55 Sine - 0x800000005e0001, // 55 Sine - 0x7fffffff7e0001, // 55 Sine - 0x7fffffff380001, // 55 Sine - 0x80000000ca0001, // 55 Sine - 0x200000000e0001, // 53 CtS - 0x20000000140001, // 53 CtS - 0x20000000280001, // 53 CtS - 0x1fffffffd80001, // 53 CtS - }, - pi: []uint64{ - 0x1fffffffffe00001, // Pi 61 - 0x1fffffffffc80001, // Pi 61 - 0x1fffffffffb40001, // Pi 61 - 0x1fffffffff500001, // Pi 61 - 0x1fffffffff420001, // Pi 61 - }, + qi: []uint64{ + 0x80000000080001, // 55 Q0 + 0xffffffffffc0001, // 60 + 0x10000000006e0001, // 60 + 0xfffffffff840001, // 60 + 0x1000000000860001, // 60 + 0xfffffffff6a0001, // 60 + 0x1000000000980001, // 60 + 0xfffffffff5a0001, // 60 + 0x1000000000b00001, // 60 StC (30) + 0x1000000000ce0001, // 60 StC (30+30) + 0x80000000440001, // 55 Sine (double angle) + 0x7fffffffba0001, // 55 Sine (double angle) + 0x80000000500001, // 55 Sine + 0x7fffffffaa0001, // 55 Sine + 0x800000005e0001, // 55 Sine + 0x7fffffff7e0001, // 55 Sine + 0x7fffffff380001, // 55 Sine + 0x80000000ca0001, // 55 Sine + 0x200000000e0001, // 53 CtS + 0x20000000140001, // 53 CtS + 0x20000000280001, // 53 CtS + 0x1fffffffd80001, // 53 CtS + }, + pi: []uint64{ + 0x1fffffffffe00001, // Pi 61 + 0x1fffffffffc80001, // Pi 61 + 0x1fffffffffb40001, // Pi 61 + 0x1fffffffff500001, // Pi 61 + 0x1fffffffff420001, // Pi 61 }, scale: 1 << 30, sigma: DefaultSigma, @@ -138,45 +134,43 @@ var DefaultBootstrappSchemeParams = []*Parameters{ { logN: 16, - logSlots: 10, - Moduli: Moduli{ - qi: []uint64{ - 0x80000000080001, // 55 Q0 - 0x2000000a0001, // 45 - 0x2000000e0001, // 45 - 0x1fffffc20001, // 45 - 0x200000440001, // 45 - 0x200000500001, // 45 - 0x200000620001, // 45 - 0x1fffff980001, // 45 - 0x2000006a0001, // 45 - 0x1fffff7e0001, // 45 - 0x100000000060001, // 56 StC (28 + 28) - 0xffa0001, // 28 StC - 0xffffffffffc0001, // 60 Sine (double angle) - 0x10000000006e0001, // 60 Sine (double angle) - 0xfffffffff840001, // 60 Sine (double angle) - 0x1000000000860001, // 60 Sine - 0xfffffffff6a0001, // 60 Sine - 0x1000000000980001, // 60 Sine - 0xfffffffff5a0001, // 60 Sine - 0x1000000000b00001, // 60 Sine - 0x1000000000ce0001, // 60 Sine - 0xfffffffff2a0001, // 60 Sine - 0xfffffffff240001, // 60 Sine - 0x200000000e0001, // 53 CtS - 0x20000000140001, // 53 CtS - 0x20000000280001, // 53 CtS - 0x1fffffffd80001, // 53 CtS - }, - pi: []uint64{ - 0x1fffffffffe00001, // Pi 61 - 0x1fffffffffc80001, // Pi 61 - 0x1fffffffffb40001, // Pi 61 - 0x1fffffffff500001, // Pi 61 - 0x1fffffffff420001, // Pi 61 - 0x1fffffffff380001, // Pi 61 - }, + logSlots: 15, + qi: []uint64{ + 0x80000000080001, // 55 Q0 + 0x2000000a0001, // 45 + 0x2000000e0001, // 45 + 0x1fffffc20001, // 45 + 0x200000440001, // 45 + 0x200000500001, // 45 + 0x200000620001, // 45 + 0x1fffff980001, // 45 + 0x2000006a0001, // 45 + 0x1fffff7e0001, // 45 + 0x100000000060001, // 56 StC (28 + 28) + 0xffa0001, // 28 StC + 0xffffffffffc0001, // 60 Sine (double angle) + 0x10000000006e0001, // 60 Sine (double angle) + 0xfffffffff840001, // 60 Sine (double angle) + 0x1000000000860001, // 60 Sine + 0xfffffffff6a0001, // 60 Sine + 0x1000000000980001, // 60 Sine + 0xfffffffff5a0001, // 60 Sine + 0x1000000000b00001, // 60 Sine + 0x1000000000ce0001, // 60 Sine + 0xfffffffff2a0001, // 60 Sine + 0xfffffffff240001, // 60 Sine + 0x200000000e0001, // 53 CtS + 0x20000000140001, // 53 CtS + 0x20000000280001, // 53 CtS + 0x1fffffffd80001, // 53 CtS + }, + pi: []uint64{ + 0x1fffffffffe00001, // Pi 61 + 0x1fffffffffc80001, // Pi 61 + 0x1fffffffffb40001, // Pi 61 + 0x1fffffffff500001, // Pi 61 + 0x1fffffffff420001, // Pi 61 + 0x1fffffffff380001, // Pi 61 }, scale: 1 << 45, sigma: DefaultSigma, @@ -184,28 +178,26 @@ var DefaultBootstrappSchemeParams = []*Parameters{ { logN: 15, - logSlots: 10, - Moduli: Moduli{ - qi: []uint64{ - 0x7fffb0001, // 35 Q0 - 0x4000000420001, // 50 - 0x1fc0001, // 25 - 0xffffffffffc0001, // 60 StC (30+30) - 0x4000000120001, // 50 Sine - 0x40000001b0001, // 50 Sine - 0x3ffffffdf0001, // 50 Sine - 0x4000000270001, // 50 Sine - 0x3ffffffd20001, // 50 Sine - 0x3ffffffcd0001, // 50 Sine - 0x4000000350001, // 50 Sine - 0x3ffffffc70001, // 50 Sine - 0x1fffffff50001, // 49 CtS - 0x1ffffffea0001, // 49 CtS - }, - pi: []uint64{ - 0x7e40000000001, // 50 - 0x7c80000000001, // 50 - }, + logSlots: 14, + qi: []uint64{ + 0x7fffb0001, // 35 Q0 + 0x4000000420001, // 50 + 0x1fc0001, // 25 + 0xffffffffffc0001, // 60 StC (30+30) + 0x4000000120001, // 50 Sine + 0x40000001b0001, // 50 Sine + 0x3ffffffdf0001, // 50 Sine + 0x4000000270001, // 50 Sine + 0x3ffffffd20001, // 50 Sine + 0x3ffffffcd0001, // 50 Sine + 0x4000000350001, // 50 Sine + 0x3ffffffc70001, // 50 Sine + 0x1fffffff50001, // 49 CtS + 0x1ffffffea0001, // 49 CtS + }, + pi: []uint64{ + 0x7e40000000001, // 50 + 0x7c80000000001, // 50 }, scale: 1 << 25, sigma: DefaultSigma, diff --git a/ckks/ckks_test.go b/ckks/ckks_test.go index 84657c01..dd61c1b5 100644 --- a/ckks/ckks_test.go +++ b/ckks/ckks_test.go @@ -178,7 +178,7 @@ func verifyTestVectors(testContext *testParams, decryptor Decryptor, valuesWant func testParameters(testContext *testParams, t *testing.T) { t.Run("Parameters/NewParametersFromModuli/", func(t *testing.T) { - p, err := NewParametersFromModuli(testContext.params.LogN(), testContext.params.Moduli) + p, err := NewParametersFromModuli(testContext.params.LogN(), testContext.params.Moduli()) p.SetLogSlots(testContext.params.LogSlots()) p.SetScale(testContext.params.Scale()) assert.NoError(t, err) @@ -443,6 +443,9 @@ func testEvaluatorRescale(testContext *testParams, t *testing.T) { values, _, ciphertext := newTestVectors(testContext, testContext.encryptorSk, complex(-1, -1), complex(1, 1), t) nbRescales := testContext.params.MaxLevel() + if nbRescales > 5 { + nbRescales = 5 + } for i := uint64(0); i < nbRescales; i++ { constant := testContext.ringQ.Modulus[ciphertext.Level()] diff --git a/ckks/params.go b/ckks/params.go index 276872f5..bb490fc2 100644 --- a/ckks/params.go +++ b/ckks/params.go @@ -38,11 +38,9 @@ var DefaultParams = []*Parameters{ //LogQi = 109 {logN: 12, logSlots: 11, - Moduli: Moduli{ // 37 + 32 - qi: []uint64{0x200000e001, - 0x100006001}, - pi: []uint64{0x3ffffea001}, // 38 - }, + qi: []uint64{0x200000e001, // 37 + 32 + 0x100006001}, + pi: []uint64{0x3ffffea001}, // 38 scale: 1 << 32, sigma: DefaultSigma, }, @@ -50,15 +48,13 @@ var DefaultParams = []*Parameters{ //LogQi = 218 {logN: 13, logSlots: 12, - Moduli: Moduli{ // 33 + 5 x 30 - qi: []uint64{0x1fffec001, - 0x3fff4001, - 0x3ffe8001, - 0x40020001, - 0x40038001, - 0x3ffc0001}, - pi: []uint64{0x800004001}, // 35 - }, + qi: []uint64{0x1fffec001, // 33 + 5 x 30 + 0x3fff4001, + 0x3ffe8001, + 0x40020001, + 0x40038001, + 0x3ffc0001}, + pi: []uint64{0x800004001}, // 35 scale: 1 << 30, sigma: DefaultSigma, }, @@ -66,14 +62,12 @@ var DefaultParams = []*Parameters{ //LogQiP = 438 {logN: 14, logSlots: 13, - Moduli: Moduli{ // 45 + 9 x 34 - qi: []uint64{0x200000008001, 0x400018001, - 0x3fffd0001, 0x400060001, - 0x400068001, 0x3fff90001, - 0x400080001, 0x4000a8001, - 0x400108001, 0x3ffeb8001}, - pi: []uint64{0x7fffffd8001, 0x7fffffc8001}, // 43, 43 - }, + qi: []uint64{0x200000008001, 0x400018001, // 45 + 9 x 34 + 0x3fffd0001, 0x400060001, + 0x400068001, 0x3fff90001, + 0x400080001, 0x4000a8001, + 0x400108001, 0x3ffeb8001}, + pi: []uint64{0x7fffffd8001, 0x7fffffc8001}, // 43, 43 scale: 1 << 34, sigma: DefaultSigma, }, @@ -81,16 +75,13 @@ var DefaultParams = []*Parameters{ //LogQi = 880 {logN: 15, logSlots: 14, - Moduli: Moduli{ // 50 + 17 x 40 - qi: []uint64{0x4000000120001, 0x10000140001, 0xffffe80001, - 0x10000290001, 0xffffc40001, 0x100003e0001, - 0x10000470001, 0x100004b0001, 0xffffb20001, - 0x10000500001, 0x10000650001, 0xffff940001, - 0xffff8a0001, 0xffff820001, 0xffff780001, - 0x10000890001, 0xffff750001, 0x10000960001}, - pi: []uint64{0x40000001b0001, 0x3ffffffdf0001, 0x4000000270001}, // 50, 50, 50 - }, - + qi: []uint64{0x4000000120001, 0x10000140001, 0xffffe80001, // 50 + 17 x 40 + 0x10000290001, 0xffffc40001, 0x100003e0001, + 0x10000470001, 0x100004b0001, 0xffffb20001, + 0x10000500001, 0x10000650001, 0xffff940001, + 0xffff8a0001, 0xffff820001, 0xffff780001, + 0x10000890001, 0xffff750001, 0x10000960001}, + pi: []uint64{0x40000001b0001, 0x3ffffffdf0001, 0x4000000270001}, // 50, 50, 50 scale: 1 << 40, sigma: DefaultSigma, }, @@ -98,18 +89,16 @@ var DefaultParams = []*Parameters{ //LogQi = 1761 {logN: 16, logSlots: 15, - Moduli: Moduli{ // 55 + 33 x 45 - qi: []uint64{0x80000000080001, 0x2000000a0001, 0x2000000e0001, 0x1fffffc20001, - 0x200000440001, 0x200000500001, 0x200000620001, 0x1fffff980001, - 0x2000006a0001, 0x1fffff7e0001, 0x200000860001, 0x200000a60001, - 0x200000aa0001, 0x200000b20001, 0x200000c80001, 0x1fffff360001, - 0x200000e20001, 0x1fffff060001, 0x200000fe0001, 0x1ffffede0001, - 0x1ffffeca0001, 0x1ffffeb40001, 0x200001520001, 0x1ffffe760001, - 0x2000019a0001, 0x1ffffe640001, 0x200001a00001, 0x1ffffe520001, - 0x200001e80001, 0x1ffffe0c0001, 0x1ffffdee0001, 0x200002480001, - 0x1ffffdb60001, 0x200002560001}, - pi: []uint64{0x80000000440001, 0x7fffffffba0001, 0x80000000500001, 0x7fffffffaa0001}, // 4 x 55 - }, + qi: []uint64{0x80000000080001, 0x2000000a0001, 0x2000000e0001, 0x1fffffc20001, // 55 + 33 x 45 + 0x200000440001, 0x200000500001, 0x200000620001, 0x1fffff980001, + 0x2000006a0001, 0x1fffff7e0001, 0x200000860001, 0x200000a60001, + 0x200000aa0001, 0x200000b20001, 0x200000c80001, 0x1fffff360001, + 0x200000e20001, 0x1fffff060001, 0x200000fe0001, 0x1ffffede0001, + 0x1ffffeca0001, 0x1ffffeb40001, 0x200001520001, 0x1ffffe760001, + 0x2000019a0001, 0x1ffffe640001, 0x200001a00001, 0x1ffffe520001, + 0x200001e80001, 0x1ffffe0c0001, 0x1ffffdee0001, 0x200002480001, + 0x1ffffdb60001, 0x200002560001}, + pi: []uint64{0x80000000440001, 0x7fffffffba0001, 0x80000000500001, 0x7fffffffaa0001}, // 4 x 55 scale: 1 << 45, sigma: DefaultSigma, }, @@ -117,142 +106,18 @@ var DefaultParams = []*Parameters{ // Moduli stores the NTT primes of the RNS representation. type Moduli struct { - qi []uint64 // Ciphertext prime moduli - pi []uint64 // Keys additional prime moduli -} - -// Qi returns a new slice with the factors of the ciphertext modulus q -func (m *Moduli) Qi() []uint64 { - qi := make([]uint64, len(m.qi)) - copy(qi, m.qi) - return qi -} - -// QiCount returns the number of factors of the ciphertext modulus Q -func (m *Moduli) QiCount() uint64 { - return uint64(len(m.qi)) -} - -// Pi returns a new slice with the factors of the ciphertext modulus extention P -func (m *Moduli) Pi() []uint64 { - pi := make([]uint64, len(m.pi)) - copy(pi, m.pi) - return pi -} - -// PiCount returns the number of factors of the ciphertext modulus extention P -func (m *Moduli) PiCount() uint64 { - return uint64(len(m.pi)) -} - -// QPiCount returns the number of factors of the ciphertext modulus + the extention modulus P -func (m *Moduli) QPiCount() uint64 { - return uint64(len(m.qi) + len(m.pi)) -} - -// LogQP returns the size of the extended modulus QP in bits -func (m *Moduli) LogQP() uint64 { - tmp := ring.NewUint(1) - for _, qi := range m.qi { - tmp.Mul(tmp, ring.NewUint(qi)) - } - for _, pi := range m.pi { - tmp.Mul(tmp, ring.NewUint(pi)) - } - return uint64(tmp.BitLen()) -} - -// LogQLvl returns the size of the modulus Q in bits at a specific level -func (m *Moduli) LogQLvl(level uint64) uint64 { - tmp := m.QLvl(level) - return uint64(tmp.BitLen()) -} - -// QLvl returns the product of the moduli at the given level as a big.Int -func (m *Moduli) QLvl(level uint64) *big.Int { - tmp := ring.NewUint(1) - for _, qi := range m.qi[:level+1] { - tmp.Mul(tmp, ring.NewUint(qi)) - } - return tmp -} - -// LogQ returns the size of the modulus Q in bits -func (m *Moduli) LogQ() uint64 { - return m.LogQLvl(m.QiCount() - 1) -} - -// Q returns the product of all the moduli as a big.Int -func (m *Moduli) Q() *big.Int { - return m.QLvl(m.QiCount() - 1) -} - -// LogP returns the size of the modulus P in bits -func (m *Moduli) LogP() uint64 { - tmp := ring.NewUint(1) - for _, pi := range m.pi { - tmp.Mul(tmp, ring.NewUint(pi)) - } - return uint64(tmp.BitLen()) -} - -// LogQAlpha returns the size in bits of the sum of the norm of -// each element of the special RNS decomposition basis for the -// key-switching. -// LogQAlpha is the size of the element that is multipled by the -// error during the keyswitching and then divided by P. -// LogQAlpha should be smaller than P or the error added during -// the key-switching wont be negligible. -func (m *Moduli) LogQAlpha() uint64 { - - alpha := m.PiCount() - - if alpha == 0 { - return 0 - } - - res := ring.NewUint(0) - var j uint64 - for i := uint64(0); i < m.QiCount(); i = i + alpha { - - j = i + alpha - if j > m.QiCount() { - j = m.QiCount() - } - - tmp := ring.NewUint(1) - for _, qi := range m.qi[i:j] { - tmp.Mul(tmp, ring.NewUint(qi)) - } - - res.Add(res, tmp) - } - - return uint64(res.BitLen()) -} - -// Alpha returns the number of moduli in in P -func (m *Moduli) Alpha() uint64 { - return m.PiCount() -} - -// Beta returns the number of element in the RNS decomposition basis: Ceil(lenQi / lenPi) -func (m *Moduli) Beta() uint64 { - if m.Alpha() != 0 { - return uint64(math.Ceil(float64(m.QiCount()) / float64(m.Alpha()))) - } - - return 0 + Qi []uint64 // Ciphertext prime moduli + Pi []uint64 // Keys additional prime moduli } // Print prints the moduli in hexadimal func (m *Moduli) Print() { - for _, qi := range m.qi { + for _, qi := range m.Qi { fmt.Printf("0x%x,\n", qi) } fmt.Println() - for _, pj := range m.pi { + for _, pj := range m.Pi { fmt.Printf("0x%x,\n", pj) } fmt.Println() @@ -261,17 +126,19 @@ func (m *Moduli) Print() { // Copy creates a copy of the target Moduli. func (m *Moduli) Copy() Moduli { - qi := make([]uint64, len(m.qi)) - copy(qi, m.qi) + qi := make([]uint64, len(m.Qi)) + copy(qi, m.Qi) - pi := make([]uint64, len(m.pi)) - copy(pi, m.pi) + pi := make([]uint64, len(m.Pi)) + copy(pi, m.Pi) return Moduli{qi, pi} } // LogModuli generates a LogModuli struct from the parameters' Moduli struct and returns it. -func (p *Parameters) LogModuli() (lm LogModuli) { +func (p *Parameters) LogModuli() (lm *LogModuli) { + + lm = new(LogModuli) lm.LogQi = make([]uint64, len(p.qi), len(p.qi)) for i := range p.qi { @@ -283,7 +150,7 @@ func (p *Parameters) LogModuli() (lm LogModuli) { lm.LogPi[i] = uint64(math.Round(math.Log2(float64(p.pi[i])))) } - return lm + return } // LogModuli stores the bit-length of the NTT primes of the RNS representation. @@ -306,7 +173,8 @@ func (m *LogModuli) Copy() LogModuli { // Parameters represents a given parameter set for the BFV cryptosystem. type Parameters struct { - Moduli + qi []uint64 + pi []uint64 logN uint64 // Ring degree (power of 2) logSlots uint64 scale float64 @@ -314,7 +182,7 @@ type Parameters struct { } // NewParametersFromModuli creates a new Parameters struct and returns a pointer to it. -func NewParametersFromModuli(logN uint64, m Moduli) (p *Parameters, err error) { +func NewParametersFromModuli(logN uint64, m *Moduli) (p *Parameters, err error) { p = new(Parameters) if (logN < 3) || (logN > MaxLogN) { @@ -327,7 +195,11 @@ func NewParametersFromModuli(logN uint64, m Moduli) (p *Parameters, err error) { return nil, err } - p.Moduli = m.Copy() + p.qi = make([]uint64, len(m.Qi), len(m.Qi)) + copy(p.qi, m.Qi) + p.pi = make([]uint64, len(m.Pi), len(m.Pi)) + copy(p.pi, m.Pi) + p.sigma = DefaultSigma return p, nil @@ -335,7 +207,7 @@ func NewParametersFromModuli(logN uint64, m Moduli) (p *Parameters, err error) { } // NewParametersFromLogModuli creates a new Parameters struct and returns a pointer to it. -func NewParametersFromLogModuli(logN uint64, lm LogModuli) (p *Parameters, err error) { +func NewParametersFromLogModuli(logN uint64, lm *LogModuli) (p *Parameters, err error) { if err = checkLogModuli(lm); err != nil { return nil, err @@ -431,6 +303,140 @@ func (p *Parameters) SetSigma(sigma float64) { p.sigma = sigma } +// Moduli returns a struct Moduli with the moduli of the parameters +func (p *Parameters) Moduli() (m *Moduli) { + m = new(Moduli) + m.Qi = make([]uint64, p.QiCount(), p.QiCount()) + m.Pi = make([]uint64, p.PiCount(), p.PiCount()) + copy(m.Qi, p.qi) + copy(m.Pi, p.pi) + return +} + +// Qi returns a new slice with the factors of the ciphertext modulus q +func (p *Parameters) Qi() []uint64 { + qi := make([]uint64, len(p.qi)) + copy(qi, p.qi) + return qi +} + +// QiCount returns the number of factors of the ciphertext modulus Q +func (p *Parameters) QiCount() uint64 { + return uint64(len(p.qi)) +} + +// Pi returns a new slice with the factors of the ciphertext modulus extention P +func (p *Parameters) Pi() []uint64 { + pi := make([]uint64, len(p.pi)) + copy(pi, p.pi) + return pi +} + +// PiCount returns the number of factors of the ciphertext modulus extention P +func (p *Parameters) PiCount() uint64 { + return uint64(len(p.pi)) +} + +// QPiCount returns the number of factors of the ciphertext modulus + the extention modulus P +func (p *Parameters) QPiCount() uint64 { + return uint64(len(p.qi) + len(p.pi)) +} + +// LogQP returns the size of the extended modulus QP in bits +func (p *Parameters) LogQP() uint64 { + tmp := ring.NewUint(1) + for _, qi := range p.qi { + tmp.Mul(tmp, ring.NewUint(qi)) + } + for _, pi := range p.pi { + tmp.Mul(tmp, ring.NewUint(pi)) + } + return uint64(tmp.BitLen()) +} + +// LogQLvl returns the size of the modulus Q in bits at a specific level +func (p *Parameters) LogQLvl(level uint64) uint64 { + tmp := p.QLvl(level) + return uint64(tmp.BitLen()) +} + +// QLvl returns the product of the moduli at the given level as a big.Int +func (p *Parameters) QLvl(level uint64) *big.Int { + tmp := ring.NewUint(1) + for _, qi := range p.qi[:level+1] { + tmp.Mul(tmp, ring.NewUint(qi)) + } + return tmp +} + +// LogQ returns the size of the modulus Q in bits +func (p *Parameters) LogQ() uint64 { + return p.LogQLvl(p.QiCount() - 1) +} + +// Q returns the product of all the moduli as a big.Int +func (p *Parameters) Q() *big.Int { + return p.QLvl(p.QiCount() - 1) +} + +// LogP returns the size of the modulus P in bits +func (p *Parameters) LogP() uint64 { + tmp := ring.NewUint(1) + for _, pi := range p.pi { + tmp.Mul(tmp, ring.NewUint(pi)) + } + return uint64(tmp.BitLen()) +} + +// LogQAlpha returns the size in bits of the sum of the norm of +// each element of the special RNS decomposition basis for the +// key-switching. +// LogQAlpha is the size of the element that is multipled by the +// error during the keyswitching and then divided by P. +// LogQAlpha should be smaller than P or the error added during +// the key-switching wont be negligible. +func (p *Parameters) LogQAlpha() uint64 { + + alpha := p.PiCount() + + if alpha == 0 { + return 0 + } + + res := ring.NewUint(0) + var j uint64 + for i := uint64(0); i < p.QiCount(); i = i + alpha { + + j = i + alpha + if j > p.QiCount() { + j = p.QiCount() + } + + tmp := ring.NewUint(1) + for _, qi := range p.pi[i:j] { + tmp.Mul(tmp, ring.NewUint(qi)) + } + + res.Add(res, tmp) + } + + return uint64(res.BitLen()) +} + +// Alpha returns the number of moduli in in P +func (p *Parameters) Alpha() uint64 { + return p.PiCount() +} + +// Beta returns the number of element in the RNS decomposition basis: Ceil(lenQi / lenPi) +func (p *Parameters) Beta() uint64 { + if p.Alpha() != 0 { + return uint64(math.Ceil(float64(p.QiCount()) / float64(p.Alpha()))) + } + + return 0 +} + // Copy creates a copy of the target parameters. func (p *Parameters) Copy() (paramsCopy *Parameters) { @@ -439,7 +445,10 @@ func (p *Parameters) Copy() (paramsCopy *Parameters) { paramsCopy.logSlots = p.logSlots paramsCopy.scale = p.scale paramsCopy.sigma = p.sigma - paramsCopy.Moduli = p.Moduli.Copy() + paramsCopy.qi = make([]uint64, len(p.qi), len(p.qi)) + copy(paramsCopy.qi, p.qi) + paramsCopy.pi = make([]uint64, len(p.pi), len(p.pi)) + copy(paramsCopy.pi, p.pi) return } @@ -512,30 +521,30 @@ func (p *Parameters) UnmarshalBinary(data []byte) (err error) { b.ReadUint64Slice(p.qi) b.ReadUint64Slice(p.pi) - if err = checkModuli(p.Moduli, p.logN); err != nil { + if err = checkModuli(p.Moduli(), p.logN); err != nil { return err } return nil } -func checkModuli(m Moduli, logN uint64) error { +func checkModuli(m *Moduli, logN uint64) error { - if len(m.qi) > MaxModuliCount { + if len(m.Qi) > MaxModuliCount { return fmt.Errorf("#Qi is larger than %d", MaxModuliCount) } - if len(m.pi) > MaxModuliCount { + if len(m.Pi) > MaxModuliCount { return fmt.Errorf("#Pi is larger than %d", MaxModuliCount) } - for i, qi := range m.qi { + for i, qi := range m.Qi { if uint64(bits.Len64(qi)-1) > MaxModuliSize+1 { return fmt.Errorf("Qi bit-size (i=%d) is larger than %d", i, MaxModuliSize) } } - for i, pi := range m.pi { + for i, pi := range m.Pi { if uint64(bits.Len64(pi)-1) > MaxModuliSize+2 { return fmt.Errorf("Pi bit-size (i=%d) is larger than %d", i, MaxModuliSize) } @@ -543,13 +552,13 @@ func checkModuli(m Moduli, logN uint64) error { N := uint64(1 << logN) - for i, qi := range m.qi { + for i, qi := range m.Qi { if !ring.IsPrime(qi) || qi&((N<<1)-1) != 1 { return fmt.Errorf("Qi (i=%d) is not an NTT prime", i) } } - for i, pi := range m.pi { + for i, pi := range m.Pi { if !ring.IsPrime(pi) || pi&((N<<1)-1) != 1 { return fmt.Errorf("Pi (i=%d) is not an NTT prime", i) } @@ -558,7 +567,7 @@ func checkModuli(m Moduli, logN uint64) error { return nil } -func checkLogModuli(m LogModuli) error { +func checkLogModuli(m *LogModuli) error { if len(m.LogQi) > MaxModuliCount { return fmt.Errorf("#LogQi is larger than %d", MaxModuliCount) @@ -583,7 +592,9 @@ func checkLogModuli(m LogModuli) error { return nil } -func genModuli(lm LogModuli, logN uint64) (m Moduli) { +func genModuli(lm *LogModuli, logN uint64) (m *Moduli) { + + m = new(Moduli) // Extracts all the different primes bit size and maps their number primesbitlen := make(map[uint64]uint64) @@ -602,16 +613,16 @@ func genModuli(lm LogModuli, logN uint64) (m Moduli) { } // Assigns the primes to the ckks moduli chain - m.qi = make([]uint64, len(lm.LogQi)) + m.Qi = make([]uint64, len(lm.LogQi)) for i, qi := range lm.LogQi { - m.qi[i] = primes[qi][0] + m.Qi[i] = primes[qi][0] primes[qi] = primes[qi][1:] } // Assigns the primes to the special primes list for the extended ring - m.pi = make([]uint64, len(lm.LogPi)) + m.Pi = make([]uint64, len(lm.LogPi)) for i, pj := range lm.LogPi { - m.pi[i] = primes[pj][0] + m.Pi[i] = primes[pj][0] primes[pj] = primes[pj][1:] } diff --git a/examples/ckks/bootstrapping/main.go b/examples/ckks/bootstrapping/main.go index c9e77a6e..97eadce9 100644 --- a/examples/ckks/bootstrapping/main.go +++ b/examples/ckks/bootstrapping/main.go @@ -35,8 +35,8 @@ func main() { // LogSlots is hardcoded to 15 in the parameters, but can be changed from 1 to 15. // When changing logSlots make sure that the number of levels allocated to CtS and StC is // smaller or equal to logSlots. - params := ckks.DefaultBootstrappSchemeParams[5] - btpParams := ckks.DefaultBootstrappParams[5] + params := ckks.DefaultBootstrappSchemeParams[0] + btpParams := ckks.DefaultBootstrappParams[0] fmt.Println() fmt.Printf("CKKS parameters : logN = %d, logSlots = %d, h = %d, logQP = %d, levels = %d, scale= 2^%f, sigma = %f \n", params.LogN(), params.LogSlots(), btpParams.H, params.LogQP(), params.Levels(), math.Log2(params.Scale()), params.Sigma()) diff --git a/examples/ckks/euler/main.go b/examples/ckks/euler/main.go index bed7c484..6d58e874 100644 --- a/examples/ckks/euler/main.go +++ b/examples/ckks/euler/main.go @@ -24,7 +24,7 @@ func example() { Scale := float64(1 << 40) - params, err := ckks.NewParametersFromLogModuli(LogN, LogModuli) + params, err := ckks.NewParametersFromLogModuli(LogN, &LogModuli) if err != nil { panic(err) }