Wrappers for polynomial evaluation

This commit is contained in:
Jean-Philippe Bossuat
2023-08-08 10:59:37 +02:00
parent d911c81b6d
commit cfd1a2f9c8
11 changed files with 116 additions and 58 deletions

View File

@@ -1,7 +1,6 @@
package circuits
import (
//"github.com/tuneinsight/lattigo/v4/bgv"
"github.com/tuneinsight/lattigo/v4/ring"
"github.com/tuneinsight/lattigo/v4/rlwe"
"github.com/tuneinsight/lattigo/v4/rlwe/ringqp"

View File

@@ -10,7 +10,6 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/tuneinsight/lattigo/v4/circuits"
"github.com/tuneinsight/lattigo/v4/ckks"
"github.com/tuneinsight/lattigo/v4/ring"
"github.com/tuneinsight/lattigo/v4/rlwe"
@@ -48,7 +47,7 @@ type ckksTestContext struct {
evaluator *ckks.Evaluator
}
func TestCKKS(t *testing.T) {
func TestFloat(t *testing.T) {
var err error
@@ -408,7 +407,7 @@ func testEvaluatePolynomial(tc *ckksTestContext, t *testing.T) {
valuesWant[j] = poly.Evaluate(values[j])
}
polyVector, err := circuits.NewPolynomialVector([]circuits.Polynomial{circuits.NewPolynomial(poly)}, slotIndex)
polyVector, err := NewPolynomialVector([]bignum.Polynomial{poly}, slotIndex)
require.NoError(t, err)
if ciphertext, err = polyEval.Polynomial(ciphertext, polyVector, ciphertext.Scale); err != nil {

View File

@@ -0,0 +1,19 @@
package float
import (
"github.com/tuneinsight/lattigo/v4/circuits"
"github.com/tuneinsight/lattigo/v4/utils/bignum"
)
type Polynomial circuits.Polynomial
func NewPolynomial(poly bignum.Polynomial) Polynomial {
return Polynomial(circuits.NewPolynomial(poly))
}
type PolynomialVector circuits.PolynomialVector
func NewPolynomialVector(polys []bignum.Polynomial, mapping map[int][]int) (PolynomialVector, error) {
p, err := circuits.NewPolynomialVector(polys, mapping)
return PolynomialVector(p), err
}

View File

@@ -15,11 +15,11 @@ type PolynomialEvaluator struct {
Parameters ckks.Parameters
}
// NewFloatPowerBasis is a wrapper of NewPolynomialBasis.
// NewPowerBasis is a wrapper of NewPolynomialBasis.
// This function creates a new powerBasis from the input ciphertext.
// The input ciphertext is treated as the base monomial X used to
// generate the other powers X^{n}.
func NewFloatPowerBasis(ct *rlwe.Ciphertext, basis bignum.Basis) circuits.PowerBasis {
func NewPowerBasis(ct *rlwe.Ciphertext, basis bignum.Basis) circuits.PowerBasis {
return circuits.NewPowerBasis(ct, basis)
}
@@ -40,8 +40,20 @@ func NewPolynomialEvaluator(params ckks.Parameters, eval circuits.EvaluatorForPo
// targetScale: the desired output scale. This value shouldn't differ too much from the original ciphertext scale. It can
// for example be used to correct small deviations in the ciphertext scale and reset it to the default scale.
func (eval PolynomialEvaluator) Polynomial(input interface{}, p interface{}, targetScale rlwe.Scale) (opOut *rlwe.Ciphertext, err error) {
var pcircuits interface{}
switch p := p.(type) {
case Polynomial:
pcircuits = circuits.Polynomial(p)
case PolynomialVector:
pcircuits = circuits.PolynomialVector(p)
default:
pcircuits = p
}
levelsConsummedPerRescaling := eval.Parameters.LevelsConsummedPerRescaling()
return circuits.EvaluatePolynomial(eval.PolynomialEvaluator, eval, input, p, targetScale, levelsConsummedPerRescaling, &simEvaluator{eval.Parameters, levelsConsummedPerRescaling})
return circuits.EvaluatePolynomial(eval.PolynomialEvaluator, eval, input, pcircuits, targetScale, levelsConsummedPerRescaling, &simEvaluator{eval.Parameters, levelsConsummedPerRescaling})
}
func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLevel int, pol circuits.PolynomialVector, pb circuits.PowerBasis, targetScale rlwe.Scale) (res *rlwe.Ciphertext, err error) {
@@ -54,7 +66,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
slots := 1 << logSlots.Cols
params := eval.Parameters
slotsIndex := pol.SlotsIndex
mapping := pol.Mapping
even := pol.IsEven()
odd := pol.IsOdd()
@@ -75,7 +87,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
}
// If an index slot is given (either multiply polynomials or masking)
if slotsIndex != nil {
if mapping != nil {
var toEncode bool
@@ -95,7 +107,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
for i, p := range pol.Value {
if !isZero(p.Coeffs[0]) {
toEncode = true
for _, j := range slotsIndex[i] {
for _, j := range mapping[i] {
values[j] = p.Coeffs[0]
}
}
@@ -125,7 +137,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
for i, p := range pol.Value {
if !isZero(p.Coeffs[0]) {
toEncode = true
for _, j := range slotsIndex[i] {
for _, j := range mapping[i] {
values[j] = p.Coeffs[0]
}
}
@@ -171,7 +183,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
// Copies the coefficient on the temporary array
// according to the slot map index
for _, j := range slotsIndex[i] {
for _, j := range mapping[i] {
values[j] = p.Coeffs[key]
}
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/tuneinsight/lattigo/v4/bfv"
"github.com/tuneinsight/lattigo/v4/bgv"
"github.com/tuneinsight/lattigo/v4/circuits"
"github.com/tuneinsight/lattigo/v4/ring"
"github.com/tuneinsight/lattigo/v4/rlwe"
"github.com/tuneinsight/lattigo/v4/utils"
@@ -280,9 +279,9 @@ func testLinearTransformation(tc *testContext, t *testing.T) {
slotIndex[0] = idx0
slotIndex[1] = idx1
polyVector, err := circuits.NewPolynomialVector([]circuits.Polynomial{
NewIntegerPolynomial(coeffs0),
NewIntegerPolynomial(coeffs1),
polyVector, err := NewPolynomialVector([][]uint64{
coeffs0,
coeffs1,
}, slotIndex)
require.NoError(t, err)

View File

@@ -7,7 +7,6 @@ import (
"testing"
"github.com/tuneinsight/lattigo/v4/bgv"
"github.com/tuneinsight/lattigo/v4/circuits"
"github.com/tuneinsight/lattigo/v4/ring"
"github.com/tuneinsight/lattigo/v4/rlwe"
"github.com/tuneinsight/lattigo/v4/utils"
@@ -365,7 +364,7 @@ func testBGVLinearTransformation(tc *bgvTestContext, t *testing.T) {
slots := values.N()
slotIndex := make(map[int][]int)
mapping := make(map[int][]int)
idx0 := make([]int, slots>>1)
idx1 := make([]int, slots>>1)
for i := 0; i < slots>>1; i++ {
@@ -373,17 +372,17 @@ func testBGVLinearTransformation(tc *bgvTestContext, t *testing.T) {
idx1[i] = 2*i + 1
}
slotIndex[0] = idx0
slotIndex[1] = idx1
mapping[0] = idx0
mapping[1] = idx1
polyVector, err := circuits.NewPolynomialVector([]circuits.Polynomial{
NewIntegerPolynomial(coeffs0),
NewIntegerPolynomial(coeffs1),
}, slotIndex)
polyVector, err := NewPolynomialVector([][]uint64{
coeffs0,
coeffs1,
}, mapping)
require.NoError(t, err)
TInt := new(big.Int).SetUint64(tc.params.PlaintextModulus())
for pol, idx := range slotIndex {
for pol, idx := range mapping {
for _, i := range idx {
values.Coeffs[0][i] = polyVector.Value[pol].EvaluateModP(new(big.Int).SetUint64(values.Coeffs[0][i]), TInt).Uint64()
}

View File

@@ -0,0 +1,26 @@
package integer
import (
"github.com/tuneinsight/lattigo/v4/circuits"
"github.com/tuneinsight/lattigo/v4/utils/bignum"
)
type Polynomial circuits.Polynomial
func NewPolynomial[T Integer](coeffs []T) Polynomial {
return Polynomial(circuits.NewPolynomial(bignum.NewPolynomial(bignum.Monomial, coeffs, nil)))
}
type PolynomialVector circuits.PolynomialVector
func NewPolynomialVector[T Integer](polys [][]T, mapping map[int][]int) (PolynomialVector, error) {
ps := make([]bignum.Polynomial, len(polys))
for i := range ps {
ps[i] = bignum.NewPolynomial(bignum.Monomial, polys[i], nil)
}
p, err := circuits.NewPolynomialVector(ps, mapping)
return PolynomialVector(p), err
}

View File

@@ -14,21 +14,14 @@ type PolynomialEvaluator struct {
InvariantTensoring bool
}
// NewIntegerPowerBasis is a wrapper of NewPolynomialBasis.
// NewPowerBasis is a wrapper of NewPolynomialBasis.
// This function creates a new powerBasis from the input ciphertext.
// The input ciphertext is treated as the base monomial X used to
// generate the other powers X^{n}.
func NewIntegerPowerBasis(ct *rlwe.Ciphertext) circuits.PowerBasis {
func NewPowerBasis(ct *rlwe.Ciphertext) circuits.PowerBasis {
return circuits.NewPowerBasis(ct, bignum.Monomial)
}
// NewIntegerPolynomial is a wrapper of NewPolynomial.
// This function creates a new polynomial from the input coefficients.
// This polynomial can be evaluated on a ciphertext.
func NewIntegerPolynomial[T Integer](coeffs []T) circuits.Polynomial {
return circuits.NewPolynomial(bignum.NewPolynomial(bignum.Monomial, coeffs, nil))
}
func NewPolynomialEvaluator(params bgv.Parameters, eval *bgv.Evaluator, InvariantTensoring bool) *PolynomialEvaluator {
e := new(PolynomialEvaluator)
@@ -44,7 +37,18 @@ func NewPolynomialEvaluator(params bgv.Parameters, eval *bgv.Evaluator, Invarian
}
func (eval PolynomialEvaluator) Polynomial(input interface{}, p interface{}, targetScale rlwe.Scale) (opOut *rlwe.Ciphertext, err error) {
return circuits.EvaluatePolynomial(eval.PolynomialEvaluator, eval, input, p, targetScale, 1, &simIntegerPolynomialEvaluator{eval.Parameters, eval.InvariantTensoring})
var pcircuits interface{}
switch p := p.(type) {
case Polynomial:
pcircuits = circuits.Polynomial(p)
case PolynomialVector:
pcircuits = circuits.PolynomialVector(p)
default:
pcircuits = p
}
return circuits.EvaluatePolynomial(eval.PolynomialEvaluator, eval, input, pcircuits, targetScale, 1, &simIntegerPolynomialEvaluator{eval.Parameters, eval.InvariantTensoring})
}
type scaleInvariantEvaluator struct {
@@ -76,7 +80,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
X := pb.Value
params := eval.Parameters
slotsIndex := pol.SlotsIndex
mapping := pol.Mapping
slots := params.RingT().N()
even := pol.IsEven()
odd := pol.IsOdd()
@@ -97,7 +101,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
}
// If an index slot is given (either multiply polynomials or masking)
if slotsIndex != nil {
if mapping != nil {
var toEncode bool
@@ -115,7 +119,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
for i, p := range pol.Value {
if c := p.Coeffs[0].Uint64(); c != 0 {
toEncode = true
for _, j := range slotsIndex[i] {
for _, j := range mapping[i] {
values[j] = c
}
}
@@ -146,7 +150,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
for i, p := range pol.Value {
if c := p.Coeffs[0].Uint64(); c != 0 {
toEncode = true
for _, j := range slotsIndex[i] {
for _, j := range mapping[i] {
values[j] = c
}
}
@@ -188,7 +192,7 @@ func (eval PolynomialEvaluator) EvaluatePolynomialVectorFromPowerBasis(targetLev
// Copies the coefficient on the temporary array
// according to the slot map index
for _, j := range slotsIndex[i] {
for _, j := range mapping[i] {
values[j] = c
}
}

View File

@@ -104,8 +104,8 @@ func (eval PolynomialEvaluator) EvaluatePatersonStockmeyerPolynomialVector(pvEva
for i := range tmp {
polyVec := PolynomialVector{
Value: make([]Polynomial, nbPoly),
SlotsIndex: poly.SlotsIndex,
Value: make([]Polynomial, nbPoly),
Mapping: poly.Mapping,
}
// Transposes the polynomial matrix

View File

@@ -134,11 +134,11 @@ func recursePS(params rlwe.ParameterProvider, logSplit, targetLevel int, p Polyn
}
type PolynomialVector struct {
Value []Polynomial
SlotsIndex map[int][]int
Value []Polynomial
Mapping map[int][]int
}
func NewPolynomialVector(polys []Polynomial, slotsIndex map[int][]int) (PolynomialVector, error) {
func NewPolynomialVector(polys []bignum.Polynomial, mapping map[int][]int) (PolynomialVector, error) {
var maxDeg int
var basis bignum.Basis
for i := range polys {
@@ -158,11 +158,13 @@ func NewPolynomialVector(polys []Polynomial, slotsIndex map[int][]int) (Polynomi
polyvec := make([]Polynomial, len(polys))
copy(polyvec, polys)
for i := range polyvec {
polyvec[i] = NewPolynomial(polys[i])
}
return PolynomialVector{
Value: polyvec,
SlotsIndex: slotsIndex,
Value: polyvec,
Mapping: mapping,
}, nil
}
@@ -191,12 +193,12 @@ func (p PolynomialVector) Factorize(n int) (polyq, polyr PolynomialVector) {
coeffsq[i], coeffsr[i] = p.Factorize(n)
}
return PolynomialVector{Value: coeffsq, SlotsIndex: p.SlotsIndex}, PolynomialVector{Value: coeffsr, SlotsIndex: p.SlotsIndex}
return PolynomialVector{Value: coeffsq, Mapping: p.Mapping}, PolynomialVector{Value: coeffsr, Mapping: p.Mapping}
}
type PatersonStockmeyerPolynomialVector struct {
Value []PatersonStockmeyerPolynomial
SlotsIndex map[int][]int
Value []PatersonStockmeyerPolynomial
Mapping map[int][]int
}
// GetPatersonStockmeyerPolynomial returns
@@ -207,7 +209,7 @@ func (p PolynomialVector) GetPatersonStockmeyerPolynomial(params rlwe.Parameters
}
return PatersonStockmeyerPolynomialVector{
Value: Value,
SlotsIndex: p.SlotsIndex,
Value: Value,
Mapping: p.Mapping,
}
}

View File

@@ -5,7 +5,6 @@ import (
"math"
"math/big"
"github.com/tuneinsight/lattigo/v4/circuits"
"github.com/tuneinsight/lattigo/v4/circuits/float"
"github.com/tuneinsight/lattigo/v4/ckks"
"github.com/tuneinsight/lattigo/v4/rlwe"
@@ -96,7 +95,7 @@ func chebyshevinterpolation() {
approxG := bignum.ChebyshevApproximation(g, interval)
// Map storing which polynomial has to be applied to which slot.
slotsIndex := make(map[int][]int)
mapping := make(map[int][]int)
idxF := make([]int, slots>>1)
idxG := make([]int, slots>>1)
@@ -105,8 +104,8 @@ func chebyshevinterpolation() {
idxG[i] = i*2 + 1 // Index with all odd slots
}
slotsIndex[0] = idxF // Assigns index of all even slots to poly[0] = f(x)
slotsIndex[1] = idxG // Assigns index of all odd slots to poly[1] = g(x)
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 {
@@ -121,7 +120,7 @@ func chebyshevinterpolation() {
panic(err)
}
polyVec, err := circuits.NewPolynomialVector([]circuits.Polynomial{circuits.NewPolynomial(approxF), circuits.NewPolynomial(approxG)}, slotsIndex)
polyVec, err := float.NewPolynomialVector([]bignum.Polynomial{approxF, approxG}, mapping)
if err != nil {
panic(err)
}