From 8144fd72563aa7d418da7d6a2c9f5669c4c66e35 Mon Sep 17 00:00:00 2001 From: Gautam Botrel Date: Thu, 5 Oct 2023 13:35:22 -0500 Subject: [PATCH] fix: handle edge case where provided domain cardinality is 1 --- ecc/bls12-377/fr/iop/ratios.go | 24 +++++++-- ecc/bls12-378/fr/iop/ratios.go | 24 +++++++-- ecc/bls12-381/fr/iop/ratios.go | 24 +++++++-- ecc/bls24-315/fr/iop/ratios.go | 24 +++++++-- ecc/bls24-317/fr/iop/ratios.go | 24 +++++++-- ecc/bn254/fr/iop/ratios.go | 24 +++++++-- ecc/bw6-633/fr/iop/ratios.go | 24 +++++++-- ecc/bw6-756/fr/iop/ratios.go | 24 +++++++-- ecc/bw6-761/fr/iop/ratios.go | 24 +++++++-- .../generator/iop/template/ratios.go.tmpl | 51 +++++++++++-------- 10 files changed, 202 insertions(+), 65 deletions(-) diff --git a/ecc/bls12-377/fr/iop/ratios.go b/ecc/bls12-377/fr/iop/ratios.go index cb235b93a..3027260bb 100644 --- a/ecc/bls12-377/fr/iop/ratios.go +++ b/ecc/bls12-377/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bls12-378/fr/iop/ratios.go b/ecc/bls12-378/fr/iop/ratios.go index e39076077..7bc6a607a 100644 --- a/ecc/bls12-378/fr/iop/ratios.go +++ b/ecc/bls12-378/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bls12-381/fr/iop/ratios.go b/ecc/bls12-381/fr/iop/ratios.go index fd66411e9..b6dabd4d6 100644 --- a/ecc/bls12-381/fr/iop/ratios.go +++ b/ecc/bls12-381/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bls24-315/fr/iop/ratios.go b/ecc/bls24-315/fr/iop/ratios.go index 3a5546ee0..889f22d45 100644 --- a/ecc/bls24-315/fr/iop/ratios.go +++ b/ecc/bls24-315/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bls24-317/fr/iop/ratios.go b/ecc/bls24-317/fr/iop/ratios.go index a7503bd8a..d0a4a030d 100644 --- a/ecc/bls24-317/fr/iop/ratios.go +++ b/ecc/bls24-317/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bn254/fr/iop/ratios.go b/ecc/bn254/fr/iop/ratios.go index 5b9a00034..b0a3721d2 100644 --- a/ecc/bn254/fr/iop/ratios.go +++ b/ecc/bn254/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bw6-633/fr/iop/ratios.go b/ecc/bw6-633/fr/iop/ratios.go index 1739f1d9e..977a10571 100644 --- a/ecc/bw6-633/fr/iop/ratios.go +++ b/ecc/bw6-633/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bw6-756/fr/iop/ratios.go b/ecc/bw6-756/fr/iop/ratios.go index 3255350ce..9e1541129 100644 --- a/ecc/bw6-756/fr/iop/ratios.go +++ b/ecc/bw6-756/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/ecc/bw6-761/fr/iop/ratios.go b/ecc/bw6-761/fr/iop/ratios.go index 3a23bde13..59ec755e3 100644 --- a/ecc/bw6-761/fr/iop/ratios.go +++ b/ecc/bw6-761/fr/iop/ratios.go @@ -329,16 +329,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } diff --git a/internal/generator/iop/template/ratios.go.tmpl b/internal/generator/iop/template/ratios.go.tmpl index a0abcffe8..dcebd51f4 100644 --- a/internal/generator/iop/template/ratios.go.tmpl +++ b/internal/generator/iop/template/ratios.go.tmpl @@ -6,7 +6,7 @@ import ( "sync" "github.com/consensys/gnark-crypto/internal/parallel" - + "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr" "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/fft" ) @@ -37,7 +37,6 @@ var ( // (Π_{k> nn) - + for j, p := range entries { idx := i if p.Layout == BitReverse { idx = iRev } - + a.Mul(&beta, &evaluationIDSmallDomain[i+j*n]). Add(&a, &gamma). Add(&a, &p.Coefficients()[idx]) - + b.Mul(&b, &a) - + c.Mul(&beta, &evaluationIDSmallDomain[permutation[i+j*n]]). Add(&c, &gamma). Add(&c, &p.Coefficients()[idx]) d.Mul(&d, &c) } - + // b = Πⱼ(Pⱼ(ωⁱ)+β*ωⁱνʲ+γ) // d = Πⱼ(Qⱼ(ωⁱ)+β*σ(j*n+i)+γ) coeffs[i+1].Set(&b) @@ -200,7 +198,7 @@ func BuildRatioCopyConstraint( } }) - chCoeffs := make(chan struct{},1) + chCoeffs := make(chan struct{}, 1) go func() { for i := 2; i < n; i++ { // ignoring coeffs[0] @@ -215,14 +213,13 @@ func BuildRatioCopyConstraint( } <-chCoeffs - // rough ratio inverse to mul; see if it makes sense to parallelize the batch inverse. + // rough ratio inverse to mul; see if it makes sense to parallelize the batch inverse. const ratioInvMul = 1000 / 17 nbTasks := runtime.NumCPU() if ratio := n / ratioInvMul; ratio < nbTasks { nbTasks = ratio } - parallel.Execute(n-1, func(start, end int) { // ignoring t[0] and coeff[0] start++ @@ -314,16 +311,30 @@ func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { // nbCopies is the number of cosets of the roots of unity that are needed, including the set of // roots of unity itself. func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { - + if nbCopies <= 0 { + panic("getSupportIdentityPermutation: nbCopies must be positive") + } + res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) sizePoly := int(domain.Cardinality) - // len(domain.Twiddle) == sizePoly / 2 - copy(res, domain.Twiddles[0]) - // remaining ones. - for i := (sizePoly/2) - 1; i < sizePoly-1; i++ { - res[i+1].Mul(&res[i], &domain.Generator) + // check if we can reuse the pre-computed twiddles from the domain. + if len(domain.Twiddles) == 0 || len(domain.Twiddles[0]) < sizePoly/2 { + res[0].SetOne() + if len(res) > 1 { + res[1].Set(&domain.Generator) + for i := 2; i < len(res); i++ { + res[i].Mul(&res[i-1], &domain.Generator) + } + } + } else { + // re-use pre-computed twiddles from the domain. + copy(res, domain.Twiddles[0]) + for i := (sizePoly / 2) - 1; i < sizePoly-1; i++ { + res[i+1].Mul(&res[i], &domain.Generator) + } } + if nbCopies <= 1 { return res } @@ -348,7 +359,7 @@ func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Elemen for j := start; j < end; j++ { res[i*sizePoly+j].Mul(&res[j], &coset) } - }, (runtime.NumCPU() / (nbCopies - 1)) + 1) + }, (runtime.NumCPU()/(nbCopies-1))+1) wg.Done() }() }