github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-378/fr/iop/ratios.go (about)

     1  // Copyright 2020 Consensys Software Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Code generated by consensys/gnark-crypto DO NOT EDIT
    16  
    17  package iop
    18  
    19  import (
    20  	"errors"
    21  	"math/big"
    22  	"math/bits"
    23  	"runtime"
    24  	"sync"
    25  
    26  	"github.com/consensys/gnark-crypto/internal/parallel"
    27  
    28  	"github.com/consensys/gnark-crypto/ecc/bls12-378/fr"
    29  	"github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft"
    30  )
    31  
    32  // errors related to the computation of the quotient and the ratios.
    33  var (
    34  	ErrMustBeRegular              = errors.New("the layout must be Regular")
    35  	ErrMustBeCanonical            = errors.New("the basis must be Canonical")
    36  	ErrMustBeLagrangeCoset        = errors.New("the basis must be LagrangeCoset")
    37  	ErrInconsistentFormat         = errors.New("the format of the polynomials must be the same")
    38  	ErrInconsistentSize           = errors.New("the sizes of the polynomial must be the same as the size of the domain")
    39  	ErrNumberPolynomials          = errors.New("the number of polynomials in the denominator and the numerator must be the same")
    40  	ErrSizeNotPowerOfTwo          = errors.New("the size of the polynomials must be a power of two")
    41  	ErrInconsistentSizeDomain     = errors.New("the size of the domain must be consistent with the size of the polynomials")
    42  	ErrIncorrectNumberOfVariables = errors.New("the number of variables is incorrect")
    43  )
    44  
    45  // Build an 'accumulating ratio' polynomial.
    46  // * numerator list of polynomials that will form the numerator of the ratio
    47  // * denominator list of polynomials that will form the denominator of the ratio
    48  // The polynomials in the denominator and the numerator are expected to be of
    49  // the same size and the size must be a power of 2. The polynomials are given as
    50  // pointers in case the caller wants to FFTInv the polynomials during the process.
    51  // * beta variable at which the numerator and denominators are evaluated
    52  // * expectedForm expected form of the resulting polynomial
    53  // * Return: say beta=β, numerator = [P₁,...,P_m], denominator = [Q₁,..,Q_m]. The function
    54  // returns a polynomial whose evaluation on the j-th root of unity is
    55  // (Π_{k<j}Π_{i<m}(β-Pᵢ(ωᵏ)))/(β-Qᵢ(ωᵏ))
    56  func BuildRatioShuffledVectors(numerator, denominator []*Polynomial, beta fr.Element, expectedForm Form, domain *fft.Domain) (*Polynomial, error) {
    57  
    58  	// check that len(numerator)=len(denominator)
    59  	if len(numerator) != len(denominator) {
    60  		return nil, ErrNumberPolynomials
    61  	}
    62  	nbPolynomials := len(numerator)
    63  
    64  	// check that the sizes are consistent
    65  	err := checkSize(numerator, denominator)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	// create the domain + some checks on the sizes of the polynomials
    71  	n := numerator[0].coefficients.Len()
    72  	domain, err = buildDomain(n, domain)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	// put every polynomials in Lagrange form. Also make sure
    78  	// that we don't modify the slices numerator and denominator, but
    79  	// only their entries. If the polynomials are unlocked, the
    80  	// entries of the slices numerator and denominator will be
    81  	// modified.
    82  	for i := 0; i < nbPolynomials; i++ {
    83  		numerator[i].ToLagrange(domain)
    84  		denominator[i].ToLagrange(domain)
    85  	}
    86  
    87  	// build the ratio (careful with the indices of
    88  	// the polynomials which are bit reversed)
    89  	coeffs := make([]fr.Element, n)
    90  	t := make([]fr.Element, n)
    91  	coeffs[0].SetOne()
    92  	t[0].SetOne()
    93  	var a, b, c, d fr.Element
    94  
    95  	nn := uint64(64 - bits.TrailingZeros(uint(n)))
    96  	for i := 0; i < n-1; i++ {
    97  
    98  		b.SetOne()
    99  		d.SetOne()
   100  
   101  		iRev := bits.Reverse64(uint64(i)) >> nn
   102  
   103  		for j := 0; j < nbPolynomials; j++ {
   104  
   105  			if numerator[j].Layout == BitReverse {
   106  				a.Sub(&beta, &numerator[j].Coefficients()[iRev])
   107  			} else {
   108  				a.Sub(&beta, &numerator[j].Coefficients()[i])
   109  			}
   110  			b.Mul(&b, &a)
   111  
   112  			if denominator[j].Layout == BitReverse {
   113  				c.Sub(&beta, &denominator[j].Coefficients()[iRev])
   114  			} else {
   115  				c.Sub(&beta, &denominator[j].Coefficients()[i])
   116  			}
   117  			d.Mul(&d, &c)
   118  		}
   119  		// b = Πₖ (β-Pₖ(ωⁱ⁻¹))
   120  		// d = Πₖ (β-Qₖ(ωⁱ⁻¹))
   121  
   122  		coeffs[i+1].Mul(&coeffs[i], &b)
   123  		t[i+1].Mul(&t[i], &d)
   124  
   125  	}
   126  
   127  	t = fr.BatchInvert(t)
   128  	for i := 1; i < n; i++ {
   129  		coeffs[i].Mul(&coeffs[i], &t[i])
   130  	}
   131  
   132  	res := NewPolynomial(&coeffs, expectedForm)
   133  
   134  	// at this stage the result is in Lagrange form, Regular layout
   135  	putInExpectedFormFromLagrangeRegular(res, domain, expectedForm)
   136  
   137  	return res, nil
   138  }
   139  
   140  // BuildRatioCopyConstraint builds the accumulating ratio polynomial to prove that
   141  // [P₁ ∥ .. ∥ P_{n—1}] is invariant by the permutation \sigma.
   142  // Namely it returns the polynomial Z whose evaluation on the j-th root of unity is
   143  // Z(ω^j) = Π_{i<j}(Π_{k<n}(P_k(ω^i)+β*u^k+γ))/(P_k(ω^i)+σ(kn+i)+γ)))
   144  // * entries list of polynomials whose evaluation are invariant under \sigma
   145  // * beta, gamma challenges
   146  // * expectedForm expected form of the resulting polynomial
   147  func BuildRatioCopyConstraint(
   148  	entries []*Polynomial,
   149  	permutation []int64,
   150  	beta, gamma fr.Element,
   151  	expectedForm Form,
   152  	domain *fft.Domain) (*Polynomial, error) {
   153  
   154  	nbPolynomials := len(entries)
   155  
   156  	// check that the sizes are consistent
   157  	err := checkSize(entries)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	// create the domain + some checks on the sizes of the polynomials
   163  	n := entries[0].coefficients.Len()
   164  	domain, err = buildDomain(n, domain)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	// put every polynomials in Lagrange form. Also make sure
   170  	// that we don't modify the slice entries
   171  	for i := 0; i < nbPolynomials; i++ {
   172  		entries[i].ToLagrange(domain)
   173  	}
   174  
   175  	// get the support for the permutation
   176  	evaluationIDSmallDomain := getSupportIdentityPermutation(nbPolynomials, domain)
   177  
   178  	// build the ratio (careful with the indices of
   179  	// the polynomials which are bit reversed)
   180  	coeffs := make([]fr.Element, n)
   181  	t := make([]fr.Element, n)
   182  	coeffs[0].SetOne()
   183  	t[0].SetOne()
   184  
   185  	parallel.Execute(n-1, func(start, end int) {
   186  		var a, b, c, d fr.Element
   187  		nn := uint64(64 - bits.TrailingZeros(uint(n)))
   188  		for i := start; i < end; i++ {
   189  			b.SetOne()
   190  			d.SetOne()
   191  
   192  			iRev := int(bits.Reverse64(uint64(i)) >> nn)
   193  
   194  			for j, p := range entries {
   195  				idx := i
   196  				if p.Layout == BitReverse {
   197  					idx = iRev
   198  				}
   199  
   200  				a.Mul(&beta, &evaluationIDSmallDomain[i+j*n]).
   201  					Add(&a, &gamma).
   202  					Add(&a, &p.Coefficients()[idx])
   203  
   204  				b.Mul(&b, &a)
   205  
   206  				c.Mul(&beta, &evaluationIDSmallDomain[permutation[i+j*n]]).
   207  					Add(&c, &gamma).
   208  					Add(&c, &p.Coefficients()[idx])
   209  				d.Mul(&d, &c)
   210  			}
   211  
   212  			// b = Πⱼ(Pⱼ(ωⁱ)+β*ωⁱνʲ+γ)
   213  			// d = Πⱼ(Qⱼ(ωⁱ)+β*σ(j*n+i)+γ)
   214  			coeffs[i+1].Set(&b)
   215  			t[i+1].Set(&d)
   216  		}
   217  	})
   218  
   219  	chCoeffs := make(chan struct{}, 1)
   220  	go func() {
   221  		for i := 2; i < n; i++ {
   222  			// ignoring coeffs[0]
   223  			coeffs[i].Mul(&coeffs[i], &coeffs[i-1])
   224  		}
   225  		close(chCoeffs)
   226  	}()
   227  
   228  	for i := 2; i < n; i++ {
   229  		// ignoring t[0]
   230  		t[i].Mul(&t[i], &t[i-1])
   231  	}
   232  	<-chCoeffs
   233  
   234  	// rough ratio inverse to mul; see if it makes sense to parallelize the batch inverse.
   235  	const ratioInvMul = 1000 / 17
   236  	nbTasks := runtime.NumCPU()
   237  	if ratio := n / ratioInvMul; ratio < nbTasks {
   238  		nbTasks = ratio
   239  	}
   240  
   241  	parallel.Execute(n-1, func(start, end int) {
   242  		// ignoring t[0] and coeff[0]
   243  		start++
   244  		end++
   245  		tInv := fr.BatchInvert(t[start:end])
   246  		for i := start; i < end; i++ {
   247  			coeffs[i].Mul(&coeffs[i], &tInv[i-start])
   248  		}
   249  	}, nbTasks)
   250  
   251  	res := NewPolynomial(&coeffs, expectedForm)
   252  	// at this stage the result is in Lagrange form, Regular layout
   253  	putInExpectedFormFromLagrangeRegular(res, domain, expectedForm)
   254  
   255  	return res, nil
   256  
   257  }
   258  
   259  func putInExpectedFormFromLagrangeRegular(p *Polynomial, domain *fft.Domain, expectedForm Form) {
   260  	p.Basis = expectedForm.Basis
   261  	p.Layout = expectedForm.Layout
   262  
   263  	if expectedForm.Basis == Canonical {
   264  		domain.FFTInverse(p.Coefficients(), fft.DIF)
   265  		if expectedForm.Layout == Regular {
   266  			fft.BitReverse(p.Coefficients())
   267  		}
   268  		return
   269  	}
   270  
   271  	if expectedForm.Basis == LagrangeCoset {
   272  		domain.FFTInverse(p.Coefficients(), fft.DIF)
   273  		domain.FFT(p.Coefficients(), fft.DIT, fft.OnCoset())
   274  		if expectedForm.Layout == BitReverse {
   275  			fft.BitReverse(p.Coefficients())
   276  		}
   277  		return
   278  	}
   279  
   280  	if expectedForm.Layout == BitReverse {
   281  		fft.BitReverse(p.Coefficients())
   282  	}
   283  
   284  }
   285  
   286  // check that the polynomials are of the same size.
   287  // It assumes that pols contains slices of the same size.
   288  func checkSize(pols ...[]*Polynomial) error {
   289  
   290  	// check sizes between one another
   291  	m := len(pols)
   292  	n := pols[0][0].coefficients.Len()
   293  	for i := 0; i < m; i++ {
   294  		for j := 0; j < len(pols); j++ {
   295  			if pols[i][j].coefficients.Len() != n {
   296  				return ErrInconsistentSize
   297  			}
   298  		}
   299  	}
   300  
   301  	return nil
   302  }
   303  
   304  // buildDomain builds the fft domain necessary to do FFTs.
   305  // n is the cardinality of the domain, it must be a power of 2.
   306  func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) {
   307  
   308  	// check if the sizes are a power of 2
   309  	if n&(n-1) != 0 {
   310  		return nil, ErrSizeNotPowerOfTwo
   311  	}
   312  
   313  	// if the domain doesn't exist we create it.
   314  	if domain == nil {
   315  		domain = fft.NewDomain(uint64(n))
   316  	}
   317  
   318  	// in case domain was not nil, it must match the size of the polynomials.
   319  	if domain.Cardinality != uint64(n) {
   320  		return nil, ErrInconsistentSizeDomain
   321  	}
   322  
   323  	return domain, nil
   324  }
   325  
   326  // getSupportIdentityPermutation returns the support on which the permutation acts.
   327  // Concretely it's X evaluated on
   328  // [1,ω,..,ωˢ⁻¹,g,g*ω,..,g*ωˢ⁻¹,..,gⁿ⁻¹,gⁿ⁻¹*ω,..,gⁿ⁻¹*ωˢ⁻¹]
   329  // nbCopies is the number of cosets of the roots of unity that are needed, including the set of
   330  // roots of unity itself.
   331  func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element {
   332  	if nbCopies <= 0 {
   333  		panic("getSupportIdentityPermutation: nbCopies must be positive")
   334  	}
   335  
   336  	res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality)
   337  	sizePoly := int(domain.Cardinality)
   338  
   339  	// TODO @gbotrel check if we can reuse the pre-computed twiddles from the domain.
   340  	res[0].SetOne()
   341  	if len(res) > 1 {
   342  		res[1].Set(&domain.Generator)
   343  		for i := 2; i < len(res); i++ {
   344  			res[i].Mul(&res[i-1], &domain.Generator)
   345  		}
   346  	}
   347  
   348  	if nbCopies <= 1 {
   349  		return res
   350  	}
   351  	var wg sync.WaitGroup
   352  	wg.Add(nbCopies - 1)
   353  	for i := 1; i < nbCopies; i++ {
   354  		i := i
   355  
   356  		var coset fr.Element
   357  		coset.Exp(domain.FrMultiplicativeGen, big.NewInt(int64(i)))
   358  
   359  		go func() {
   360  			parallel.Execute(sizePoly, func(start, end int) {
   361  				for j := start; j < end; j++ {
   362  					res[i*sizePoly+j].Mul(&res[j], &coset)
   363  				}
   364  			}, (runtime.NumCPU()/(nbCopies-1))+1)
   365  			wg.Done()
   366  		}()
   367  	}
   368  
   369  	wg.Wait()
   370  
   371  	return res
   372  }