github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-381/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-381/fr" 29 "github.com/consensys/gnark-crypto/ecc/bls12-381/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 }