github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-381/fr/fft/fft_test.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 fft 18 19 import ( 20 "math/big" 21 "strconv" 22 "testing" 23 24 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" 25 26 "github.com/leanovate/gopter" 27 "github.com/leanovate/gopter/gen" 28 "github.com/leanovate/gopter/prop" 29 30 "fmt" 31 ) 32 33 func TestFFT(t *testing.T) { 34 parameters := gopter.DefaultTestParameters() 35 parameters.MinSuccessfulTests = 5 36 properties := gopter.NewProperties(parameters) 37 38 for maxSize := 2; maxSize <= 1<<10; maxSize <<= 1 { 39 40 domainWithPrecompute := NewDomain(uint64(maxSize)) 41 domainWithoutPrecompute := NewDomain(uint64(maxSize), WithoutPrecompute()) 42 43 for domainName, domain := range map[string]*Domain{ 44 "with precompute": domainWithPrecompute, 45 "without precompute": domainWithoutPrecompute, 46 } { 47 domainName := domainName 48 domain := domain 49 t.Logf("domain: %s", domainName) 50 properties.Property("DIF FFT should be consistent with dual basis", prop.ForAll( 51 52 // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result 53 func(ithpower int) bool { 54 55 pol := make([]fr.Element, maxSize) 56 backupPol := make([]fr.Element, maxSize) 57 58 for i := 0; i < maxSize; i++ { 59 pol[i].SetRandom() 60 } 61 copy(backupPol, pol) 62 63 domain.FFT(pol, DIF) 64 BitReverse(pol) 65 66 sample := domain.Generator 67 sample.Exp(sample, big.NewInt(int64(ithpower))) 68 69 eval := evaluatePolynomial(backupPol, sample) 70 71 return eval.Equal(&pol[ithpower]) 72 73 }, 74 gen.IntRange(0, maxSize-1), 75 )) 76 77 properties.Property("DIF FFT on cosets should be consistent with dual basis", prop.ForAll( 78 79 // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result 80 func(ithpower int) bool { 81 82 pol := make([]fr.Element, maxSize) 83 backupPol := make([]fr.Element, maxSize) 84 85 for i := 0; i < maxSize; i++ { 86 pol[i].SetRandom() 87 } 88 copy(backupPol, pol) 89 90 domain.FFT(pol, DIF, OnCoset()) 91 BitReverse(pol) 92 93 sample := domain.Generator 94 sample.Exp(sample, big.NewInt(int64(ithpower))). 95 Mul(&sample, &domain.FrMultiplicativeGen) 96 97 eval := evaluatePolynomial(backupPol, sample) 98 99 return eval.Equal(&pol[ithpower]) 100 101 }, 102 gen.IntRange(0, maxSize-1), 103 )) 104 105 properties.Property("DIT FFT should be consistent with dual basis", prop.ForAll( 106 107 // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result 108 func(ithpower int) bool { 109 110 pol := make([]fr.Element, maxSize) 111 backupPol := make([]fr.Element, maxSize) 112 113 for i := 0; i < maxSize; i++ { 114 pol[i].SetRandom() 115 } 116 copy(backupPol, pol) 117 118 BitReverse(pol) 119 domain.FFT(pol, DIT) 120 121 sample := domain.Generator 122 sample.Exp(sample, big.NewInt(int64(ithpower))) 123 124 eval := evaluatePolynomial(backupPol, sample) 125 126 return eval.Equal(&pol[ithpower]) 127 128 }, 129 gen.IntRange(0, maxSize-1), 130 )) 131 132 properties.Property("bitReverse(DIF FFT(DIT FFT (bitReverse))))==id", prop.ForAll( 133 134 func() bool { 135 136 pol := make([]fr.Element, maxSize) 137 backupPol := make([]fr.Element, maxSize) 138 139 for i := 0; i < maxSize; i++ { 140 pol[i].SetRandom() 141 } 142 copy(backupPol, pol) 143 144 BitReverse(pol) 145 domain.FFT(pol, DIT) 146 domain.FFTInverse(pol, DIF) 147 BitReverse(pol) 148 149 check := true 150 for i := 0; i < len(pol); i++ { 151 check = check && pol[i].Equal(&backupPol[i]) 152 } 153 return check 154 }, 155 )) 156 157 for nbCosets := 2; nbCosets < 5; nbCosets++ { 158 properties.Property(fmt.Sprintf("bitReverse(DIF FFT(DIT FFT (bitReverse))))==id on %d cosets", nbCosets), prop.ForAll( 159 160 func() bool { 161 162 pol := make([]fr.Element, maxSize) 163 backupPol := make([]fr.Element, maxSize) 164 165 for i := 0; i < maxSize; i++ { 166 pol[i].SetRandom() 167 } 168 copy(backupPol, pol) 169 170 check := true 171 172 for i := 1; i <= nbCosets; i++ { 173 174 BitReverse(pol) 175 domain.FFT(pol, DIT, OnCoset()) 176 domain.FFTInverse(pol, DIF, OnCoset()) 177 BitReverse(pol) 178 179 for i := 0; i < len(pol); i++ { 180 check = check && pol[i].Equal(&backupPol[i]) 181 } 182 } 183 184 return check 185 }, 186 )) 187 } 188 189 properties.Property("DIT FFT(DIF FFT)==id", prop.ForAll( 190 191 func() bool { 192 193 pol := make([]fr.Element, maxSize) 194 backupPol := make([]fr.Element, maxSize) 195 196 for i := 0; i < maxSize; i++ { 197 pol[i].SetRandom() 198 } 199 copy(backupPol, pol) 200 201 domain.FFTInverse(pol, DIF) 202 domain.FFT(pol, DIT) 203 204 check := true 205 for i := 0; i < len(pol); i++ { 206 check = check && (pol[i] == backupPol[i]) 207 } 208 return check 209 }, 210 )) 211 212 properties.Property("DIT FFT(DIF FFT)==id on cosets", prop.ForAll( 213 214 func() bool { 215 216 pol := make([]fr.Element, maxSize) 217 backupPol := make([]fr.Element, maxSize) 218 219 for i := 0; i < maxSize; i++ { 220 pol[i].SetRandom() 221 } 222 copy(backupPol, pol) 223 224 domain.FFTInverse(pol, DIF, OnCoset()) 225 domain.FFT(pol, DIT, OnCoset()) 226 227 for i := 0; i < len(pol); i++ { 228 if !(pol[i].Equal(&backupPol[i])) { 229 return false 230 } 231 } 232 233 // compute with nbTasks == 1 234 domain.FFTInverse(pol, DIF, OnCoset(), WithNbTasks(1)) 235 domain.FFT(pol, DIT, OnCoset(), WithNbTasks(1)) 236 237 for i := 0; i < len(pol); i++ { 238 if !(pol[i].Equal(&backupPol[i])) { 239 return false 240 } 241 } 242 243 return true 244 }, 245 )) 246 } 247 properties.TestingRun(t, gopter.ConsoleReporter(false)) 248 } 249 250 } 251 252 // -------------------------------------------------------------------- 253 // benches 254 255 func BenchmarkFFT(b *testing.B) { 256 257 const maxSize = 1 << 20 258 259 pol := make([]fr.Element, maxSize) 260 pol[0].SetRandom() 261 for i := 1; i < maxSize; i++ { 262 pol[i] = pol[i-1] 263 } 264 265 for i := 8; i < 20; i++ { 266 sizeDomain := 1 << i 267 b.Run("fft 2**"+strconv.Itoa(i)+"bits", func(b *testing.B) { 268 domain := NewDomain(uint64(sizeDomain)) 269 b.ResetTimer() 270 for j := 0; j < b.N; j++ { 271 domain.FFT(pol[:sizeDomain], DIT) 272 } 273 }) 274 b.Run("fft 2**"+strconv.Itoa(i)+"bits (coset)", func(b *testing.B) { 275 domain := NewDomain(uint64(sizeDomain)) 276 b.ResetTimer() 277 for j := 0; j < b.N; j++ { 278 domain.FFT(pol[:sizeDomain], DIT, OnCoset()) 279 } 280 }) 281 } 282 283 } 284 285 func BenchmarkFFTDITCosetReference(b *testing.B) { 286 const maxSize = 1 << 20 287 288 pol := make([]fr.Element, maxSize) 289 pol[0].SetRandom() 290 for i := 1; i < maxSize; i++ { 291 pol[i] = pol[i-1] 292 } 293 294 domain := NewDomain(maxSize) 295 296 b.ResetTimer() 297 for j := 0; j < b.N; j++ { 298 domain.FFT(pol, DIT, OnCoset()) 299 } 300 } 301 302 func BenchmarkFFTDIFReference(b *testing.B) { 303 const maxSize = 1 << 20 304 305 pol := make([]fr.Element, maxSize) 306 pol[0].SetRandom() 307 for i := 1; i < maxSize; i++ { 308 pol[i] = pol[i-1] 309 } 310 311 domain := NewDomain(maxSize) 312 313 b.ResetTimer() 314 for j := 0; j < b.N; j++ { 315 domain.FFT(pol, DIF) 316 } 317 } 318 319 func evaluatePolynomial(pol []fr.Element, val fr.Element) fr.Element { 320 var acc, res, tmp fr.Element 321 res.Set(&pol[0]) 322 acc.Set(&val) 323 for i := 1; i < len(pol); i++ { 324 tmp.Mul(&acc, &pol[i]) 325 res.Add(&res, &tmp) 326 acc.Mul(&acc, &val) 327 } 328 return res 329 }