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

     1  import (
     2  	"testing"
     3  
     4  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr"
     5  	"github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/fft"
     6  )
     7  
     8  // getPermutation returns a deterministic permutation
     9  // of n elements where n is even. The result should be
    10  // interpreted as
    11  // a permutation σ(i)=permutation[i]
    12  // g is a generator of ℤ/nℤ
    13  func getPermutation(n, g int) []int {
    14  
    15  	res := make([]int, n)
    16  	a := g
    17  	for i := 0; i < n; i++ {
    18  		res[i] = a
    19  		a += g
    20  		a %= n
    21  	}
    22  	return res
    23  }
    24  
    25  func getPermutedPolynomials(sizePolynomials, nbPolynomials int) ([]*Polynomial, []*Polynomial, []int) {
    26  
    27  	numerator := make([]*Polynomial, nbPolynomials)
    28  	for i := 0; i < nbPolynomials; i++ {
    29  		numerator[i] = NewPolynomial(randomVector(sizePolynomials), Form{Basis:Lagrange, Layout:Regular})
    30  	}
    31  
    32  	// get permutation
    33  	sigma := getPermutation(sizePolynomials*nbPolynomials, 3)
    34  
    35  	// the denominator is the permuted version of the numerators
    36  	// concatenated
    37  	denominator := make([]*Polynomial, nbPolynomials)
    38  	for i := 0; i < nbPolynomials; i++ {
    39  		denominator[i] = NewPolynomial(randomVector(sizePolynomials), Form{Basis:Lagrange, Layout:Regular})
    40  	}
    41  	for i := 0; i < len(sigma); i++ {
    42  		id := int(sigma[i] / sizePolynomials)
    43  		od := sigma[i] % sizePolynomials
    44  		in := int(i / sizePolynomials)
    45  		on := i % sizePolynomials
    46  		denominator[in].Coefficients()[on].Set(&numerator[id].Coefficients()[od])
    47  	}
    48  
    49  	return numerator, denominator, sigma
    50  
    51  }
    52  
    53  func TestBuildRatioShuffledVectors(t *testing.T) {
    54  
    55  	// generate random vectors, interpreted in Lagrange form,
    56  	// regular layout. It is enough for this test if TestPutInLagrangeForm
    57  	// passes.
    58  	sizePolynomials := 8
    59  	nbPolynomials := 4
    60  	numerator, denominator, _ := getPermutedPolynomials(sizePolynomials, nbPolynomials)
    61  
    62  	// save the originals for further tests with polynomials in different forms
    63  	backupNumerator := make([]*Polynomial, nbPolynomials)
    64  	backupDenominator := make([]*Polynomial, nbPolynomials)
    65  	for i := 0; i < nbPolynomials; i++ {
    66  		backupNumerator[i] = numerator[i].Clone()
    67  		backupDenominator[i] = denominator[i].Clone()
    68  	}
    69  
    70  	// build the ratio polynomial
    71  	expectedForm := Form{Basis: Lagrange, Layout: Regular}
    72  	domain := fft.NewDomain(uint64(sizePolynomials))
    73  	var beta fr.Element
    74  	beta.SetRandom()
    75  	ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain)
    76  	if err != nil {
    77  		t.Fatal()
    78  	}
    79  
    80  	// check that the whole product is equal to one
    81  	var a, b, c, d fr.Element
    82  	b.SetOne()
    83  	d.SetOne()
    84  	for i := 0; i < nbPolynomials; i++ {
    85  		a.Sub(&beta, &numerator[i].Coefficients()[sizePolynomials-1])
    86  		b.Mul(&a, &b)
    87  		c.Sub(&beta, &denominator[i].Coefficients()[sizePolynomials-1])
    88  		d.Mul(&c, &d)
    89  	}
    90  	a.Mul(&b, &ratio.Coefficients()[sizePolynomials-1]).
    91  		Div(&a, &d)
    92  	var one fr.Element
    93  	one.SetOne()
    94  	if !a.Equal(&one) {
    95  		t.Fatal("accumulating ratio is not equal to one")
    96  	}
    97  
    98  	// check that the ratio is correct when the inputs are
    99  	// bit reversed
   100  	for i := 0; i < nbPolynomials; i++ {
   101  		numerator[i] = backupNumerator[i].Clone()
   102  		fft.BitReverse(numerator[i].Coefficients())
   103  		numerator[i].Layout = BitReverse
   104  
   105  		denominator[i] = backupDenominator[i].Clone()
   106  		fft.BitReverse(denominator[i].Coefficients())
   107  		denominator[i].Layout = BitReverse
   108  	}
   109  	{
   110  		var err error
   111  		_ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain)
   112  		if err != nil {
   113  			t.Fatal(err)
   114  		}
   115  		checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients)
   116  		if !checkCoeffs {
   117  			t.Fatal(err)
   118  		}
   119  	}
   120  
   121  	// check that the ratio is correct when the inputs are in
   122  	// canonical form, regular
   123  	for i := 0; i < nbPolynomials; i++ {
   124  		numerator[i] = backupNumerator[i].Clone()
   125  		domain.FFTInverse(numerator[i].Coefficients(), fft.DIF)
   126  		fft.BitReverse(numerator[i].Coefficients())
   127  		numerator[i].Basis = Canonical
   128  
   129  		denominator[i] = backupDenominator[i].Clone()
   130  		domain.FFTInverse(denominator[i].Coefficients(), fft.DIF)
   131  		fft.BitReverse(denominator[i].Coefficients())
   132  		denominator[i].Basis = Canonical
   133  	}
   134  	{
   135  		var err error
   136  		_ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain)
   137  		if err != nil {
   138  			t.Fatal(err)
   139  		}
   140  		checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients)
   141  		if !checkCoeffs {
   142  			t.Fatal("coefficients of ratio are not consistent")
   143  		}
   144  	}
   145  
   146  	// check that the ratio is correct when the inputs are in
   147  	// canonical form, bit reverse
   148  	for i := 0; i < nbPolynomials; i++ {
   149  		numerator[i] = backupNumerator[i].Clone()
   150  		domain.FFTInverse(numerator[i].Coefficients(), fft.DIF)
   151  		numerator[i].Layout = BitReverse
   152  		numerator[i].Basis = Canonical
   153  
   154  		denominator[i] = backupDenominator[i].Clone()
   155  		domain.FFTInverse(denominator[i].Coefficients(), fft.DIF)
   156  		denominator[i].Layout = BitReverse
   157  		denominator[i].Basis = Canonical
   158  	}
   159  	{
   160  		var err error
   161  		_ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain)
   162  		if err != nil {
   163  			t.Fatal(err)
   164  		}
   165  		checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients)
   166  		if !checkCoeffs {
   167  			t.Fatal("coefficients of ratio are not consistent")
   168  		}
   169  	}
   170  
   171  }
   172  
   173  // sizePolynomial*nbPolynomial must be divisible by 2.
   174  // The function generates a list of nbPolynomials (P_i) of size n=sizePolynomials
   175  // such that [P₁ ∥ .. ∥ P₂ ] is invariant under the permutation
   176  // σ defined by:
   177  // σ = (12)(34)..(2n-1 2n)
   178  // so σ is a product of cycles length 2.
   179  func getInvariantEntriesUnderPermutation(sizePolynomials, nbPolynomials int) ([]*Polynomial, []int64) {
   180  	res := make([]*Polynomial, nbPolynomials)
   181  	form := Form{Layout: Regular, Basis: Lagrange}
   182  	for i := 0; i < nbPolynomials; i++ {
   183  		v := make([]fr.Element, sizePolynomials)
   184  		res[i] = NewPolynomial(&v, form)
   185  		for j := 0; j < sizePolynomials/2; j++ {
   186  			res[i].Coefficients()[2*j].SetRandom()
   187  			res[i].Coefficients()[2*j+1].Set(&res[i].Coefficients()[2*j])
   188  		}
   189  	}
   190  	permutation := make([]int64, nbPolynomials*sizePolynomials)
   191  	for i := int64(0); i < int64(nbPolynomials*sizePolynomials/2); i++ {
   192  		permutation[2*i] = 2*i + 1
   193  		permutation[2*i+1] = 2 * i
   194  	}
   195  	return res, permutation
   196  }
   197  
   198  func TestBuildRatioCopyConstraint(t *testing.T) {
   199  
   200  	// generate random vectors, interpreted in Lagrange form,
   201  	// regular layout. It is enough for this test if TestPutInLagrangeForm
   202  	// passes.
   203  	sizePolynomials := 8
   204  	nbPolynomials := 4
   205  	entries, sigma := getInvariantEntriesUnderPermutation(sizePolynomials, nbPolynomials)
   206  
   207  	// save the originals for further tests with polynomials in different forms
   208  	backupEntries := make([]*Polynomial, nbPolynomials)
   209  	for i := 0; i < nbPolynomials; i++ {
   210  		backupEntries[i] = entries[i].Clone()
   211  	}
   212  
   213  	// build the ratio polynomial
   214  	expectedForm := Form{Basis: Lagrange, Layout: Regular}
   215  	domain := fft.NewDomain(uint64(sizePolynomials))
   216  	var beta, gamma fr.Element
   217  	beta.SetRandom()
   218  	gamma.SetRandom()
   219  	ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain)
   220  	if err != nil {
   221  		t.Fatal()
   222  	}
   223  
   224  	// check that the whole product is equal to one
   225  	suppID := getSupportIdentityPermutation(nbPolynomials, domain)
   226  	var a, b, c, d fr.Element
   227  	b.SetOne()
   228  	d.SetOne()
   229  	for i := 0; i < nbPolynomials; i++ {
   230  		a.Mul(&beta, &suppID[(i+1)*sizePolynomials-1]).
   231  			Add(&a, &entries[i].Coefficients()[sizePolynomials-1]).
   232  			Add(&a, &gamma)
   233  		b.Mul(&b, &a)
   234  
   235  		c.Mul(&beta, &suppID[sigma[(i+1)*sizePolynomials-1]]).
   236  			Add(&c, &entries[i].Coefficients()[sizePolynomials-1]).
   237  			Add(&c, &gamma)
   238  		d.Mul(&d, &c)
   239  	}
   240  	a.Mul(&b, &ratio.Coefficients()[sizePolynomials-1]).
   241  		Div(&a, &d)
   242  	var one fr.Element
   243  	one.SetOne()
   244  	if !a.Equal(&one) {
   245  		t.Fatal("accumulating ratio is not equal to one")
   246  	}
   247  
   248  	// check that the ratio is correct when the inputs are
   249  	// bit reversed
   250  	for i := 0; i < nbPolynomials; i++ {
   251  		entries[i] = backupEntries[i].Clone()
   252  		fft.BitReverse(entries[i].Coefficients())
   253  		entries[i].Layout = BitReverse
   254  	}
   255  	{
   256  		var err error
   257  		_ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain)
   258  		if err != nil {
   259  			t.Fatal(err)
   260  		}
   261  		checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients)
   262  		if !checkCoeffs {
   263  			t.Fatal(err)
   264  		}
   265  	}
   266  
   267  	// check that the ratio is correct when the inputs are in
   268  	// canonical form, regular
   269  	for i := 0; i < nbPolynomials; i++ {
   270  		entries[i] = backupEntries[i].Clone()
   271  		domain.FFTInverse(entries[i].Coefficients(), fft.DIF)
   272  		fft.BitReverse(entries[i].Coefficients())
   273  		entries[i].Layout = Regular
   274  		entries[i].Basis = Canonical
   275  	}
   276  	{
   277  		var err error
   278  		_ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain)
   279  		if err != nil {
   280  			t.Fatal(err)
   281  		}
   282  		checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients)
   283  		if !checkCoeffs {
   284  			t.Fatal("coefficients of ratio are not consistent")
   285  		}
   286  	}
   287  
   288  	// check that the ratio is correct when the inputs are in
   289  	// canonical form, bit reverse
   290  	for i := 0; i < nbPolynomials; i++ {
   291  		entries[i] = backupEntries[i].Clone()
   292  		domain.FFTInverse(entries[i].Coefficients(), fft.DIF)
   293  		entries[i].Layout = BitReverse
   294  		entries[i].Basis = Canonical
   295  	}
   296  
   297  	{
   298  		var err error
   299  		_ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain)
   300  		if err != nil {
   301  			t.Fatal(err)
   302  		}
   303  		checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients)
   304  		if !checkCoeffs {
   305  			t.Fatal("coefficients of ratio are not consistent")
   306  		}
   307  	}
   308  }