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 }