Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: direct Fp6 extension for BW6-761 #1126

Merged
merged 26 commits into from
May 8, 2024
Merged

Conversation

yelhousni
Copy link
Contributor

@yelhousni yelhousni commented May 3, 2024

Description

In this PR we use a mix of towers to do arithmetic over Fp6. For multiplication we use Toom-6 and for squaring we use Complex over Chung-Hasan SQR2 (quadratic over cubic). For sparse multiplications we use custom functions with mapping to the direct extension. We also experimented with Karatsuba, Toom-3 and Montgomery-6 but kept the best options given the Plonkish constraints model (additions are not free, multiplications by constants are free...) and the emulated arithmetic cost (subtractions are costlier than additions, multiplication by large constants does add constraints...).

refs.:

image

Type of change

  • New feature (non-breaking change which adds functionality)

How has this been tested?

  • Existing tests for Fp6 mul and square pass.
  • Added a test to cross check Montgomery-6 against Toom-6.

How has this been benchmarked?

  • double pairing circuit in BN254 i.e. e(P1,Q1) * e(P2,Q2):
old new
13,451,059 scs 12,963,879 scs
  • BW6-761 PLONK verifier circuit in BN254:
old new
32,145,454 scs 31,607,491 scs

Checklist:

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • I did not modify files generated from templates
  • golangci-lint does not output errors locally
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@yelhousni yelhousni self-assigned this May 3, 2024
@yelhousni yelhousni added this to the v0.9.0 milestone May 3, 2024
@yelhousni yelhousni requested review from ivokub and gbotrel May 4, 2024 04:40
@ivokub
Copy link
Collaborator

ivokub commented May 7, 2024

Suggested edit:

diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go
index 976d4f9d..433b92cc 100644
--- a/std/algebra/emulated/fields_bw6761/e6.go
+++ b/std/algebra/emulated/fields_bw6761/e6.go
@@ -182,7 +182,7 @@ func (e Ext6) Conjugate(x *E6) *E6 {
 	}
 }
 
-func mulFpByNonResidue(fp *curveF, x *baseEl) *baseEl {
+func (e Ext6) mulFpByNonResidue(fp *curveF, x *baseEl) *baseEl {
 
 	z := fp.Neg(x)
 	z = fp.MulConst(z, big.NewInt(4))
@@ -739,7 +739,7 @@ func (e Ext6) Square(x *E6) *E6 {
 	tmp = e.fp.Add(&x.A1, &x.A3)
 	c1 = e.fp.Mul(c1, tmp)
 	c1 = e.fp.Sub(c1, e.fp.Add(t0, t1))
-	t2 = mulFpByNonResidue(e.fp, t2)
+	t2 = e.mulFpByNonResidue(e.fp, t2)
 	// c2
 	c20 := e.fp.Add(c0, t0)
 	c21 := e.fp.Add(c1, t2)
@@ -761,7 +761,7 @@ func (e Ext6) Square(x *E6) *E6 {
 	tmp = e.fp.Add(c30, c31)
 	c1 = e.fp.Mul(c1, tmp)
 	c1 = e.fp.Sub(c1, e.fp.Add(t0, t1))
-	t2 = mulFpByNonResidue(e.fp, t2)
+	t2 = e.mulFpByNonResidue(e.fp, t2)
 	c00 = e.fp.Add(c0, t0)
 	c01 = e.fp.Add(c1, t2)
 	c02 = e.fp.Add(c2, t1)
@@ -797,7 +797,7 @@ func (e Ext6) CyclotomicSquareKarabina12345(x *E6) *E6 {
 	// h4 = -g4 + 3((g3+g5)(g1+c*g2)-g1g5-c*g3g2)
 	g1g5 := e.fp.Mul(&x.A2, &x.A5)
 	g3g2 := e.fp.Mul(&x.A1, &x.A4)
-	h4 := mulFpByNonResidue(e.fp, &x.A4)
+	h4 := e.mulFpByNonResidue(e.fp, &x.A4)
 	h4 = e.fp.Add(h4, &x.A2)
 	t := e.fp.Add(&x.A1, &x.A5)
 	h4 = e.fp.Mul(h4, t)
@@ -808,13 +808,13 @@ func (e Ext6) CyclotomicSquareKarabina12345(x *E6) *E6 {
 	h4 = e.fp.Sub(h4, &x.A3)
 
 	// h3 = 2(g3+3c*g1g5)
-	h3 := mulFpByNonResidue(e.fp, g1g5)
+	h3 := e.mulFpByNonResidue(e.fp, g1g5)
 	h3 = e.fp.MulConst(h3, big.NewInt(3))
 	h3 = e.fp.Add(h3, &x.A1)
 	h3 = e.fp.MulConst(h3, big.NewInt(2))
 
 	// h2 = 3((g1+g5)(g1+c*g5)-(c+1)*g1g5)-2g2
-	t = mulFpByNonResidue(e.fp, &x.A5)
+	t = e.mulFpByNonResidue(e.fp, &x.A5)
 	t = e.fp.Add(t, &x.A2)
 	h2 := e.fp.Add(&x.A5, &x.A2)
 	h2 = e.fp.Mul(h2, t)
@@ -825,7 +825,7 @@ func (e Ext6) CyclotomicSquareKarabina12345(x *E6) *E6 {
 	h2 = e.fp.Sub(h2, t)
 
 	// h1 = 3((g3+g2)(g3+c*g2)-(c+1)*g3g2)-2g1
-	t = mulFpByNonResidue(e.fp, &x.A4)
+	t = e.mulFpByNonResidue(e.fp, &x.A4)
 	t = e.fp.Add(t, &x.A1)
 	h1 := e.fp.Add(&x.A4, &x.A1)
 	h1 = e.fp.Mul(h1, t)
diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go
index 3365e900..12cbf4ab 100644
--- a/std/algebra/emulated/fields_bw6761/e6_pairing.go
+++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go
@@ -1,8 +1,9 @@
 package fields_bw6761
 
 import (
-	"github.com/consensys/gnark/std/math/emulated"
 	"math/big"
+
+	"github.com/consensys/gnark/std/math/emulated"
 )
 
 func (e Ext6) nSquareKarabina12345(z *E6, n int) *E6 {
@@ -252,7 +253,7 @@ func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 {
 	c1 = e.fp.Mul(c1, tmp)
 	c1 = e.fp.Sub(c1, t0)
 	c1 = e.fp.Sub(c1, t1)
-	t2 = mulFpByNonResidue(e.fp, t2)
+	t2 = e.mulFpByNonResidue(e.fp, t2)
 	a0 = e.fp.Add(c0, t0)
 	a1 = e.fp.Add(c1, t2)
 	a2 = e.fp.Add(c2, t1)
@@ -276,7 +277,7 @@ func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 {
 	c1 = e.fp.Mul(c1, tmp)
 	c1 = e.fp.Sub(c1, t0)
 	c1 = e.fp.Sub(c1, t1)
-	t2 = mulFpByNonResidue(e.fp, t2)
+	t2 = e.mulFpByNonResidue(e.fp, t2)
 	b0 := e.fp.Add(c0, t0)
 	b1 = e.fp.Add(c1, t2)
 	b2 = e.fp.Add(c2, t1)
@@ -292,7 +293,7 @@ func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 {
 	c1 = e.fp.Add(&z.A1, &z.A3)
 	c1 = e.fp.Mul(c1, x[2])
 	c1 = e.fp.Sub(c1, t1)
-	tmp = mulFpByNonResidue(e.fp, t2)
+	tmp = e.mulFpByNonResidue(e.fp, t2)
 	c1 = e.fp.Add(c1, tmp)
 	tmp = e.fp.Add(&z.A1, &z.A5)
 	c2 = e.fp.Mul(x[4], tmp)
@@ -306,7 +307,7 @@ func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 {
 	tmp = e.fp.Add(b2, c2)
 	z12 := e.fp.Sub(a2, tmp)
 
-	z00 := mulFpByNonResidue(e.fp, c2)
+	z00 := e.mulFpByNonResidue(e.fp, c2)
 	z00 = e.fp.Add(z00, b0)
 	z01 := e.fp.Add(c0, b1)
 	z02 := e.fp.Add(c1, b2)

Copy link
Collaborator

@ivokub ivokub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion for making mulFpByNonResidue as a method of Ext6. But otherwise imo looks all perfect. I didn't check through all the equations closely, but the hinted computation seems to be properly and fully constrained and the tests are all good (plus the additional test ensures that new methods are complete), so I think it is good to go!

Great work! I guess with the direct extension now we can more easily try out the polynomial identity checking variants of multiplications also.

@yelhousni yelhousni merged commit 971577c into master May 8, 2024
7 checks passed
@yelhousni yelhousni deleted the perf/direct-extensions branch May 8, 2024 22:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants