github.com/consensys/gnark-crypto@v0.14.0/internal/generator/iop/template/polynomial.go.tmpl (about)

     1  import (
     2  	"math/big"
     3  	"math/bits"
     4  	"io"
     5  	"encoding/binary"
     6  	"runtime"
     7  
     8  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr"
     9  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/fft"
    10  )
    11  
    12  // Basis indicates the basis in which a polynomial is represented.
    13  type Basis uint32
    14  
    15  const (
    16  	Canonical Basis = 1 << iota
    17  	Lagrange
    18  	LagrangeCoset
    19  )
    20  
    21  // Layout indicates if a polynomial has a BitReverse or a Regular layout
    22  type Layout uint32
    23  
    24  const (
    25  	Regular Layout = 8 << iota
    26  	BitReverse
    27  )
    28  
    29  // Form describes the form of a polynomial.
    30  // TODO should be a regular enum?
    31  type Form struct {
    32  	Basis  Basis
    33  	Layout Layout
    34  }
    35  
    36  // enum of the possible Form values for type-safe switches
    37  // in this package
    38  var (
    39  	canonicalRegular        = Form{Canonical, Regular}
    40  	canonicalBitReverse     = Form{Canonical, BitReverse}
    41  	lagrangeRegular         = Form{Lagrange, Regular}
    42  	lagrangeBitReverse      = Form{Lagrange, BitReverse}
    43  	lagrangeCosetRegular    = Form{LagrangeCoset, Regular}
    44  	lagrangeCosetBitReverse = Form{LagrangeCoset, BitReverse}
    45  )
    46  
    47  // Polynomial wraps a polynomial so that it is
    48  // interpreted as P'(X)=P(\omega^{s}X).
    49  // Size is the real size of the polynomial (seen as a vector).
    50  // For instance if len(P)=32 but P.Size=8, it means that P has been
    51  // extended (e.g. it is evaluated on a larger set) but P is a polynomial
    52  // of degree 7.
    53  // blindedSize is the size of the polynomial when it is blinded. By
    54  // default blindedSize=Size, until the polynomial is blinded.
    55  type Polynomial struct {
    56  	*polynomial
    57  	shift       int
    58  	size        int
    59  	blindedSize int
    60  }
    61  
    62  // NewPolynomial returned a Polynomial from the provided coefficients in the given form.
    63  // A Polynomial can be seen as a "shared pointer" on a list of coefficients.
    64  // It is the responsibility of the user to call the Clone method if the coefficients
    65  // shouldn't be mutated.
    66  func NewPolynomial(coeffs *[]fr.Element, form Form) *Polynomial {
    67  	return &Polynomial{
    68  		polynomial:  newPolynomial(coeffs, form),
    69  		size:        len(*coeffs),
    70  		blindedSize: len(*coeffs),
    71  	}
    72  }
    73  
    74  // Shift the wrapped polynomial; it doesn't modify the underlying data structure,
    75  // but flag the Polynomial such that it will be interpreted as p(\omega^shift X)
    76  func (p *Polynomial) Shift(shift int) *Polynomial {
    77  	p.shift = shift
    78  	return p
    79  }
    80  
    81  // BlindedSize returns the the size of the polynomial when it is blinded. By
    82  // default blindedSize=Size, until the polynomial is blinded.
    83  func (p *Polynomial) BlindedSize() int {
    84  	return p.blindedSize
    85  }
    86  
    87  // Size returns the real size of the polynomial (seen as a vector).
    88  // For instance if len(P)=32 but P.Size=8, it means that P has been
    89  // extended (e.g. it is evaluated on a larger set) but P is a polynomial
    90  // of degree 7.
    91  func (p *Polynomial) Size() int {
    92  	return p.size
    93  }
    94  
    95  // SetSize sets the size of the polynomial.
    96  // size is the real size of the polynomial (seen as a vector).
    97  // For instance if len(P)=32 but P.size=8, it means that P has been
    98  // extended (e.g. it is evaluated on a larger set) but P is a polynomial
    99  // of degree 7.
   100  func (p *Polynomial) SetSize(size int) {
   101  	p.size = size
   102  }
   103  
   104  // SetBlindedSize sets the blinded size of the polynomial.
   105  func (p *Polynomial) SetBlindedSize(size int) {
   106  	p.blindedSize = size
   107  }
   108  
   109  // Blind blinds a polynomial q by adding Q(X)*(X^{n}-1),
   110  // where deg Q = blindingOrder and Q is random, and n is the
   111  // size of q. Sets the result to p and returns it.
   112  //
   113  // blindingOrder is the degree of Q, where the blinding is Q(X)*(X^{n}-1)
   114  // where n is the size of p. The size of p is modified since the underlying
   115  // polynomial is of bigger degree now. The new size is p.Size+1+blindingOrder.
   116  //
   117  // /!\ The code panics if wq is not in canonical, regular layout
   118  func (p *Polynomial) Blind(blindingOrder int) *Polynomial {
   119  	// check that p is in canonical basis
   120  	if p.Form != canonicalRegular {
   121  		panic("the input must be in canonical basis, regular layout")
   122  	}
   123  
   124  	// we add Q*(x^{n}-1) so the new size is deg(Q)+n+1
   125  	// where n is the size of wq.
   126  	newSize := p.size + blindingOrder + 1
   127  
   128  	// Resize p. The size of wq might has already been increased
   129  	// (e.g. when the polynomial is evaluated on a larger domain),
   130  	// if that's the case we don't resize the polynomial.
   131  	p.grow(newSize)
   132  
   133  	// blinding: we add Q(X)(X^{n}-1) to P, where deg(Q)=blindingOrder
   134  	var r fr.Element
   135  
   136  	for i := 0; i <= blindingOrder; i++ {
   137  		r.SetRandom()
   138  		(*p.coefficients)[i].Sub(&(*p.coefficients)[i], &r)
   139  		(*p.coefficients)[i+p.size].Add(&(*p.coefficients)[i+p.size], &r)
   140  	}
   141  	p.blindedSize = newSize
   142  
   143  	return p
   144  }
   145  
   146  // Evaluate evaluates p at x.
   147  // The code panics if the function is not in canonical form.
   148  func (p *Polynomial) Evaluate(x fr.Element) fr.Element {
   149  
   150  	if p.shift == 0 {
   151  		return p.polynomial.evaluate(x)
   152  	}
   153  
   154  	var g fr.Element
   155  	if p.shift <= 5 {
   156  		gen, err := fft.Generator(uint64(p.size))
   157  		if err != nil {
   158  			panic(err)
   159  		}
   160  		g = smallExp(gen, p.shift)
   161  		x.Mul(&x, &g)
   162  		return p.polynomial.evaluate(x)
   163  	}
   164  
   165  	bs := big.NewInt(int64(p.shift))
   166  	g = *g.Exp(g, bs)
   167  	x.Mul(&x, &g)
   168  	return p.polynomial.evaluate(x)
   169  }
   170  
   171  // Clone returns a deep copy of p. The underlying polynomial is cloned;
   172  // see also ShallowClone to perform a ShallowClone on the underlying polynomial.
   173  // If capacity is provided, the new coefficient slice capacity will be set accordingly.
   174  func (p *Polynomial) Clone(capacity ...int) *Polynomial {
   175  	res := p.ShallowClone()
   176  	res.polynomial = p.polynomial.clone(capacity...)
   177  	return res
   178  }
   179  
   180  // ShallowClone returns a shallow copy of p. The underlying polynomial coefficient
   181  // is NOT cloned and both objects will point to the same coefficient vector.
   182  func (p *Polynomial) ShallowClone() *Polynomial {
   183  	res := *p
   184  	return &res
   185  }
   186  
   187  // GetCoeff returns the i-th entry of p, taking the layout in account.
   188  func (p *Polynomial) GetCoeff(i int) fr.Element {
   189  
   190  	n := p.coefficients.Len()
   191  	rho := n / p.size
   192  	if p.polynomial.Form.Layout == Regular {
   193  		return (*p.coefficients)[(i+rho*p.shift)%n]
   194  	} else {
   195  		nn := uint64(64 - bits.TrailingZeros(uint(n)))
   196  		iRev := bits.Reverse64(uint64((i+rho*p.shift)%n)) >> nn
   197  		return (*p.coefficients)[iRev]
   198  	}
   199  
   200  }
   201  
   202  // polynomial represents a polynomial, the vector of coefficients
   203  // along with the basis and the layout.
   204  type polynomial struct {
   205  	coefficients *fr.Vector
   206  	Form
   207  }
   208  
   209  // Coefficients returns a slice on the underlying data structure.
   210  func (p *polynomial) Coefficients() []fr.Element {
   211  	return (*p.coefficients)
   212  }
   213  
   214  // newPolynomial creates a new polynomial. The slice coeff NOT copied
   215  // but directly assigned to the new polynomial.
   216  func newPolynomial(coeffs *[]fr.Element, form Form) *polynomial {
   217  	return &polynomial{coefficients: (*fr.Vector)(coeffs), Form: form}
   218  }
   219  
   220  // clone returns a deep copy of the underlying data structure.
   221  func (p *polynomial) clone(capacity ...int) *polynomial {
   222  	c := p.coefficients.Len()
   223  	if len(capacity) == 1 && capacity[0] > c {
   224  		c = capacity[0]
   225  	}
   226  	newCoeffs := make(fr.Vector, p.coefficients.Len(), c)
   227  	r := &polynomial{
   228  		coefficients: &newCoeffs,
   229  		Form:         p.Form,
   230  	}
   231  	copy((*r.coefficients), (*p.coefficients))
   232  	return r
   233  }
   234  
   235  // evaluate evaluates p at x.
   236  // The code panics if the function is not in canonical form.
   237  func (p *polynomial) evaluate(x fr.Element) fr.Element {
   238  
   239  	var r fr.Element
   240  	if p.Basis != Canonical {
   241  		panic("p must be in canonical basis")
   242  	}
   243  
   244  	if p.Layout == Regular {
   245  		for i := p.coefficients.Len() - 1; i >= 0; i-- {
   246  			r.Mul(&r, &x).Add(&r, &(*p.coefficients)[i])
   247  		}
   248  	} else {
   249  		nn := uint64(64 - bits.TrailingZeros(uint(p.coefficients.Len())))
   250  		for i := p.coefficients.Len() - 1; i >= 0; i-- {
   251  			iRev := bits.Reverse64(uint64(i)) >> nn
   252  			r.Mul(&r, &x).Add(&r, &(*p.coefficients)[iRev])
   253  		}
   254  	}
   255  
   256  	return r
   257  
   258  }
   259  
   260  // ToRegular changes the layout of p to Regular.
   261  // Leaves p unchanged if p's layout was already Regular.
   262  func (p *Polynomial) ToRegular() *Polynomial {
   263  	if p.Layout == Regular {
   264  		return p
   265  	}
   266  	fft.BitReverse((*p.coefficients))
   267  	p.Layout = Regular
   268  	return p
   269  }
   270  
   271  // ToBitReverse changes the layout of p to BitReverse.
   272  // Leaves p unchanged if p's layout was already BitReverse.
   273  func (p *Polynomial) ToBitReverse() *Polynomial {
   274  	if p.Layout == BitReverse {
   275  		return p
   276  	}
   277  	fft.BitReverse((*p.coefficients))
   278  	p.Layout = BitReverse
   279  	return p
   280  }
   281  
   282  // ToLagrange converts p to Lagrange form.
   283  // Leaves p unchanged if p was already in Lagrange form.
   284  func (p *Polynomial) ToLagrange(d *fft.Domain, nbTasks ...int) *Polynomial {
   285  	id := p.Form
   286  	p.grow(int(d.Cardinality))
   287  
   288  	n := runtime.NumCPU()
   289  	if len(nbTasks) > 0 {
   290  		n = nbTasks[0]
   291  	}
   292  
   293  	switch id {
   294  	case canonicalRegular:
   295  		p.Layout = BitReverse
   296  		d.FFT((*p.coefficients), fft.DIF, fft.WithNbTasks(n))
   297  	case canonicalBitReverse:
   298  		p.Layout = Regular
   299  		d.FFT((*p.coefficients), fft.DIT, fft.WithNbTasks(n))
   300  	case lagrangeRegular, lagrangeBitReverse:
   301  		return p
   302  	case lagrangeCosetRegular:
   303  		p.Layout = Regular
   304  		d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n))
   305  		d.FFT((*p.coefficients), fft.DIT)
   306  	case lagrangeCosetBitReverse:
   307  		p.Layout = BitReverse
   308  		d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n))
   309  		d.FFT((*p.coefficients), fft.DIF)
   310  	default:
   311  		panic("unknown ID")
   312  	}
   313  	p.Basis = Lagrange
   314  	return p
   315  }
   316  
   317  // ToCanonical converts p to canonical form.
   318  // Leaves p unchanged if p was already in Canonical form.
   319  func (p *Polynomial) ToCanonical(d *fft.Domain, nbTasks ...int) *Polynomial {
   320  	id := p.Form
   321  	p.grow(int(d.Cardinality))
   322  	n := runtime.NumCPU()
   323  	if len(nbTasks) > 0 {
   324  		n = nbTasks[0]
   325  	}
   326  	switch id {
   327  	case canonicalRegular, canonicalBitReverse:
   328  		return p
   329  	case lagrangeRegular:
   330  		p.Layout = BitReverse
   331  		d.FFTInverse((*p.coefficients), fft.DIF, fft.WithNbTasks(n))
   332  	case lagrangeBitReverse:
   333  		p.Layout = Regular
   334  		d.FFTInverse((*p.coefficients), fft.DIT, fft.WithNbTasks(n))
   335  	case lagrangeCosetRegular:
   336  		p.Layout = BitReverse
   337  		d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n))
   338  	case lagrangeCosetBitReverse:
   339  		p.Layout = Regular
   340  		d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n))
   341  	default:
   342  		panic("unknown ID")
   343  	}
   344  	p.Basis = Canonical
   345  	return p
   346  }
   347  
   348  func (p *polynomial) grow(newSize int) {
   349  	offset := newSize - p.coefficients.Len()
   350  	if offset > 0 {
   351  		(*p.coefficients) = append((*p.coefficients), make(fr.Vector, offset)...)
   352  	}
   353  }
   354  
   355  // ToLagrangeCoset Sets p to q, in LagrangeCoset form and returns it.
   356  func (p *Polynomial) ToLagrangeCoset(d *fft.Domain) *Polynomial {
   357  	id := p.Form
   358  	p.grow(int(d.Cardinality))
   359  	switch id {
   360  	case canonicalRegular:
   361  		p.Layout = BitReverse
   362  		d.FFT((*p.coefficients), fft.DIF, fft.OnCoset())
   363  	case canonicalBitReverse:
   364  		p.Layout = Regular
   365  		d.FFT((*p.coefficients), fft.DIT, fft.OnCoset())
   366  	case lagrangeRegular:
   367  		p.Layout = Regular
   368  		d.FFTInverse((*p.coefficients), fft.DIF)
   369  		d.FFT((*p.coefficients), fft.DIT, fft.OnCoset())
   370  	case lagrangeBitReverse:
   371  		p.Layout = BitReverse
   372  		d.FFTInverse((*p.coefficients), fft.DIT)
   373  		d.FFT((*p.coefficients), fft.DIF, fft.OnCoset())
   374  	case lagrangeCosetRegular, lagrangeCosetBitReverse:
   375  		return p
   376  	default:
   377  		panic("unknown ID")
   378  	}
   379  
   380  	p.Basis = LagrangeCoset
   381  	return p
   382  }
   383  
   384  // WriteTo implements io.WriterTo
   385  func (p *Polynomial) WriteTo(w io.Writer) (int64, error) {
   386  	// encode coefficients
   387  	n, err := p.polynomial.coefficients.WriteTo(w)
   388  	if err != nil {
   389  		return n, err
   390  	}
   391  
   392  	// encode Form.Basis, Form.Layout, shift, size & blindedSize as uint32
   393  	var data = []uint32{
   394  		uint32(p.Basis),
   395  		uint32(p.Layout),
   396  		uint32(p.shift),
   397  		uint32(p.size),
   398  		uint32(p.blindedSize),
   399  	}
   400  	for _, v := range data {
   401  		err = binary.Write(w, binary.BigEndian, v)
   402  		if err != nil {
   403  			return n, err
   404  		}
   405  		n += 4
   406  	}
   407  	return n, nil
   408  }
   409  
   410  // ReadFrom implements io.ReaderFrom
   411  func (p *Polynomial) ReadFrom(r io.Reader) (int64, error) {
   412  	// decode coefficients
   413  	if p.polynomial == nil {
   414  		p.polynomial = new(polynomial)
   415  	}
   416  	if p.polynomial.coefficients == nil {
   417  		v := make(fr.Vector, 0)
   418  		p.polynomial.coefficients = &v
   419  	}
   420  	n, err := p.polynomial.coefficients.ReadFrom(r)
   421  	if err != nil {
   422  		return n, err
   423  	}
   424  
   425  	// decode Form.Basis, Form.Layout, shift, size & blindedSize as uint32
   426  	var data [5]uint32
   427  	var buf [4]byte
   428  	for i := range data {
   429  		read, err := io.ReadFull(r, buf[:4])
   430  		n += int64(read)
   431  		if err != nil {
   432  			return n, err
   433  		}
   434  		data[i] = binary.BigEndian.Uint32(buf[:4])
   435  	}
   436  
   437  	p.Basis = Basis(data[0])
   438  	p.Layout = Layout(data[1])
   439  	p.shift = int(data[2])
   440  	p.size = int(data[3])
   441  	p.blindedSize = int(data[4])
   442  
   443  	return n, nil
   444  }