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  }