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: BW6 pairing computation using non-native Eval #1312

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions internal/stats/latest_stats.csv
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,14 @@ pairing_bn254,bls24_315,plonk,0,0
pairing_bn254,bls24_317,plonk,0,0
pairing_bn254,bw6_761,plonk,0,0
pairing_bn254,bw6_633,plonk,0,0
pairing_bw6761,bn254,groth16,3014749,4979960
pairing_bw6761,bn254,groth16,1843705,3084217
pairing_bw6761,bls12_377,groth16,0,0
pairing_bw6761,bls12_381,groth16,0,0
pairing_bw6761,bls24_315,groth16,0,0
pairing_bw6761,bls24_317,groth16,0,0
pairing_bw6761,bw6_761,groth16,0,0
pairing_bw6761,bw6_633,groth16,0,0
pairing_bw6761,bn254,plonk,11486969,10777222
pairing_bw6761,bn254,plonk,6947630,6315782
pairing_bw6761,bls12_377,plonk,0,0
pairing_bw6761,bls12_381,plonk,0,0
pairing_bw6761,bls24_315,plonk,0,0
Expand Down
137 changes: 134 additions & 3 deletions std/algebra/emulated/fields_bw6761/e6.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,7 @@ func (e Ext6) mulFpByNonResidue(fp *curveF, x *baseEl) *baseEl {
}

func (e Ext6) Mul(x, y *E6) *E6 {
x = e.Reduce(x)
y = e.Reduce(y)
return e.mulToomCook6(x, y)
return e.mulDirect(x, y)
}

func (e Ext6) mulMontgomery6(x, y *E6) *E6 {
Expand Down Expand Up @@ -426,6 +424,37 @@ func (e Ext6) mulMontgomery6(x, y *E6) *E6 {
}
}

func (e Ext6) mulDirect(x, y *E6) *E6 {
nonResidue := e.fp.NewElement(-4)
// c0 = a0b0 + β(a1b5 + a2b4 + a3b3 + a4b2 + a5b1)
c0 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A0}, {nonResidue, &x.A1, &y.A5}, {nonResidue, &x.A2, &y.A4}, {nonResidue, &x.A3, &y.A3}, {nonResidue, &x.A4, &y.A2}, {nonResidue, &x.A5, &y.A1}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c1 = a0b1 + a1b0 + β(a2b5 + a3b4 + a4b3 + a5b2)
c1 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A1}, {&x.A1, &y.A0}, {nonResidue, &x.A2, &y.A5}, {nonResidue, &x.A3, &y.A4}, {nonResidue, &x.A4, &y.A3}, {nonResidue, &x.A5, &y.A2}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c2 = a0b2 + a1b1 + a2b0 + β(a3b5 + a4b4 + a5b3)
c2 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A2}, {&x.A1, &y.A1}, {&x.A2, &y.A0}, {nonResidue, &x.A3, &y.A5}, {nonResidue, &x.A4, &y.A4}, {nonResidue, &x.A5, &y.A3}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c3 = a0b3 + a1b2 + a2b1 + a3b0 + β(a4b5 + a5b4)
c3 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A3}, {&x.A1, &y.A2}, {&x.A2, &y.A1}, {&x.A3, &y.A0}, {nonResidue, &x.A4, &y.A5}, {nonResidue, &x.A5, &y.A4}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c4 = a0b4 + a1b3 + a2b2 + a3b1 + a4b0 + βa5b5
c4 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A4}, {&x.A1, &y.A3}, {&x.A2, &y.A2}, {&x.A3, &y.A1}, {&x.A4, &y.A0}, {nonResidue, &x.A5, &y.A5}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c5 = a0b5 + a1b4 + a2b3 + a3b2 + a4b1 + a5b0,
c5 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A5}, {&x.A1, &y.A4}, {&x.A2, &y.A3}, {&x.A3, &y.A2}, {&x.A4, &y.A1}, {&x.A5, &y.A0}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})

return &E6{
A0: *c0,
A1: *c1,
A2: *c2,
A3: *c3,
A4: *c4,
A5: *c5,
}
}

func (e Ext6) mulToomCook6(x, y *E6) *E6 {
// Toom-Cook 6-way multiplication:
//
Expand Down Expand Up @@ -704,6 +733,43 @@ func (e Ext6) mulToomCook6(x, y *E6) *E6 {
}

func (e Ext6) Square(x *E6) *E6 {
// return e.squarePolyWithRand(x, e.fp.NewElement(-1))
return e.squareDirect(x)
}

// squareDirect computes the square of an element in E6 using schoolbook multiplication.
func (e Ext6) squareDirect(x *E6) *E6 {
nonResidue := e.fp.NewElement(-4)
// c0 = a0b0 + β(a1b5 + a2b4 + a3b3 + a4b2 + a5b1)
c0 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A0}, {nonResidue, &x.A1, &x.A5}, {nonResidue, &x.A2, &x.A4}, {nonResidue, &x.A3, &x.A3}, {nonResidue, &x.A4, &x.A2}, {nonResidue, &x.A5, &x.A1}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c1 = a0b1 + a1b0 + β(a2b5 + a3b4 + a4b3 + a5b2)
c1 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A1}, {&x.A1, &x.A0}, {nonResidue, &x.A2, &x.A5}, {nonResidue, &x.A3, &x.A4}, {nonResidue, &x.A4, &x.A3}, {nonResidue, &x.A5, &x.A2}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c2 = a0b2 + a1b1 + a2b0 + β(a3b5 + a4b4 + a5b3)
c2 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A2}, {&x.A1, &x.A1}, {&x.A2, &x.A0}, {nonResidue, &x.A3, &x.A5}, {nonResidue, &x.A4, &x.A4}, {nonResidue, &x.A5, &x.A3}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c3 = a0b3 + a1b2 + a2b1 + a3b0 + β(a4b5 + a5b4)
c3 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A3}, {&x.A1, &x.A2}, {&x.A2, &x.A1}, {&x.A3, &x.A0}, {nonResidue, &x.A4, &x.A5}, {nonResidue, &x.A5, &x.A4}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c4 = a0b4 + a1b3 + a2b2 + a3b1 + a4b0 + βa5b5
c4 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A4}, {&x.A1, &x.A3}, {&x.A2, &x.A2}, {&x.A3, &x.A1}, {&x.A4, &x.A0}, {nonResidue, &x.A5, &x.A5}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c5 = a0b5 + a1b4 + a2b3 + a3b2 + a4b1 + a5b0,
c5 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A5}, {&x.A1, &x.A4}, {&x.A2, &x.A3}, {&x.A3, &x.A2}, {&x.A4, &x.A1}, {&x.A5, &x.A0}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})

return &E6{
A0: *c0,
A1: *c1,
A2: *c2,
A3: *c3,
A4: *c4,
A5: *c5,
}
}

func (e Ext6) squareEmulatedTower(x *E6) *E6 {
// We don't use Montgomery-6 or Toom-Cook-6 for the squaring but instead we
// simulate a quadratic over cubic extension tower because Karatsuba over
// Chung-Hasan SQR2 is better constraint wise.
Expand Down Expand Up @@ -792,6 +858,45 @@ func (e Ext6) Square(x *E6) *E6 {
// https://eprint.iacr.org/2010/542.pdf
// Sec. 5.6 with minor modifications to fit our tower
func (e Ext6) CyclotomicSquareKarabina12345(x *E6) *E6 {
return e.cyclotomicSquareKarabina12345Eval(x)
}

// cyclotomicSquareKarabina12345Eval computes
// [Ext6.cyclotomicSquareKarabina12345] but with the non-native Eval method.
func (e Ext6) cyclotomicSquareKarabina12345Eval(x *E6) *E6 {
c := e.fp.NewElement(-4)
mone := e.fp.NewElement(-1)
g1 := x.A2
g2 := x.A4
g3 := x.A1
g4 := x.A3
g5 := x.A5
// h1 = 3*c*g2^2 + 3*g3^2 - 2*g1
h1 := e.fp.Eval([][]*baseEl{{c, &g2, &g2}, {&g3, &g3}, {mone, &g1}}, []*big.Int{big.NewInt(3), big.NewInt(3), big.NewInt(2)})
// h2 = 3*c*g5^2 + 3*g1^2 - 2*g2
h2 := e.fp.Eval([][]*baseEl{{c, &g5, &g5}, {&g1, &g1}, {mone, &g2}}, []*big.Int{big.NewInt(3), big.NewInt(3), big.NewInt(2)})
// h3 = 6*c*g1*g5 + 2*g3
h3 := e.fp.Eval([][]*baseEl{{c, &g1, &g5}, {&g3}}, []*big.Int{big.NewInt(6), big.NewInt(2)})
// h4 = 3*c*g2*g5 + 3*g1*g3 - g4
h4 := e.fp.Eval([][]*baseEl{{c, &g2, &g5}, {&g1, &g3}, {mone, &g4}}, []*big.Int{big.NewInt(3), big.NewInt(3), big.NewInt(1)})
// h5 = 6*g2*g3 + 2*g5
h5 := e.fp.Eval([][]*baseEl{{&g2, &g3}, {&g5}}, []*big.Int{big.NewInt(6), big.NewInt(2)})

return &E6{
A0: x.A0,
A1: *h3,
A2: *h1,
A3: *h4,
A4: *h2,
A5: *h5,
}

}

// Karabina's compressed cyclotomic square SQR12345
// https://eprint.iacr.org/2010/542.pdf
// Sec. 5.6 with minor modifications to fit our tower
func (e Ext6) cyclotomicSquareKarabina12345(x *E6) *E6 {
x = e.Reduce(x)

// h4 = -g4 + 3((g3+g5)(g1+c*g2)-g1g5-c*g3g2)
Expand Down Expand Up @@ -852,6 +957,32 @@ func (e Ext6) CyclotomicSquareKarabina12345(x *E6) *E6 {

// DecompressKarabina12345 decompresses Karabina's cyclotomic square result SQR12345
func (e Ext6) DecompressKarabina12345(x *E6) *E6 {
return e.decompressKarabina12345Eval(x)
}

// decompressKarabina12345Eval computes [Ext6.DecompressKarabina12345] but with the non-native Eval method.
func (e Ext6) decompressKarabina12345Eval(x *E6) *E6 {
mone := e.fp.NewElement(-1)
c := e.fp.NewElement(-4)
g1 := x.A2
g2 := x.A4
g3 := x.A1
g4 := x.A3
g5 := x.A5
// h0 = -3*c*g1*g2 + 2*c*g4^2 + c*g3*g5 + 1
h0 := e.fp.Eval([][]*baseEl{{mone, c, &g1, &g2}, {c, &g4, &g4}, {c, &g3, &g5}, {e.fp.One()}}, []*big.Int{big.NewInt(3), big.NewInt(2), big.NewInt(1), big.NewInt(1)})
return &E6{
A0: *h0,
A1: g3,
A2: g1,
A3: g4,
A4: g2,
A5: g5,
}
}

// DecompressKarabina12345 decompresses Karabina's cyclotomic square result SQR12345
func (e Ext6) decompressKarabina12345(x *E6) *E6 {
x = e.Reduce(x)

// h0 = (2g4^2 + g3g5 - 3g2g1)*c + 1
Expand Down
105 changes: 102 additions & 3 deletions std/algebra/emulated/fields_bw6761/e6_pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,38 @@ func (e Ext6) ExpC2(z *E6) *E6 {
//
// E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0}
func (e *Ext6) MulBy023(z *E6, c0, c1 *baseEl) *E6 {
return e.mulBy023Direct(z, c0, c1)
}

// mulBy023Direct multiplies z by an E6 sparse element 023 using schoolbook multiplication
func (e Ext6) mulBy023Direct(z *E6, c0, c1 *baseEl) *E6 {
nonResidue := e.fp.NewElement(-4)

// z0 = a0c0 + β(a3 + a4c1)
z0 := e.fp.Eval([][]*baseEl{{&z.A0, c0}, {nonResidue, &z.A3}, {nonResidue, &z.A4, c1}}, []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// z1 = a1c0 + β(a4 + a5c1)
z1 := e.fp.Eval([][]*baseEl{{&z.A1, c0}, {nonResidue, &z.A4}, {nonResidue, &z.A5, c1}}, []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// z2 = a0c1 + a2c0 + β(a5)
z2 := e.fp.Eval([][]*baseEl{{&z.A0, c1}, {&z.A2, c0}, {nonResidue, &z.A5}}, []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c3 = a0 + a1c1 + a3c0
z3 := e.fp.Eval([][]*baseEl{{&z.A0}, {&z.A1, c1}, {&z.A3, c0}}, []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c4 = a1 + a2c1 + a4c0
z4 := e.fp.Eval([][]*baseEl{{&z.A1}, {&z.A2, c1}, {&z.A4, c0}}, []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c5 = a2 + a3c1 + a5c0,
z5 := e.fp.Eval([][]*baseEl{{&z.A2}, {&z.A3, c1}, {&z.A5, c0}}, []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})

return &E6{
A0: *z0,
A1: *z1,
A2: *z2,
A3: *z3,
A4: *z4,
A5: *z5,
}
}

// mulBy023 multiplies z by an E6 sparse element 023
func (e Ext6) mulBy023(z *E6, c0, c1 *baseEl) *E6 {
z = e.Reduce(z)

a := e.fp.Mul(&z.A0, c0)
Expand Down Expand Up @@ -198,14 +230,42 @@ func (e *Ext6) MulBy023(z *E6, c0, c1 *baseEl) *E6 {

}

// Mul023By023 multiplies two E6 sparse element of the form:
// Mul023By023 multiplies two E6 sparse element of the form:
//
// E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0}
//
// and
//
// E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0}
func (e Ext6) Mul023By023(d0, d1, c0, c1 *baseEl) [5]*baseEl {
return e.mul023by023Direct(d0, d1, c0, c1)
}

// mul023by023Direct multiplies two E6 sparse element using schoolbook multiplication
func (e Ext6) mul023by023Direct(d0, d1, c0, c1 *baseEl) [5]*baseEl {
nonResidue := e.fp.NewElement(-4)
// c0 = d0c0 + β
z0 := e.fp.Eval([][]*baseEl{{d0, c0}, {nonResidue}}, []*big.Int{big.NewInt(1), big.NewInt(1)})
// c2 = d0c1 + d1c0
z2 := e.fp.Eval([][]*baseEl{{d0, c1}, {d1, c0}}, []*big.Int{big.NewInt(1), big.NewInt(1)})
// c3 = d0 + c0
z3 := e.fp.Add(d0, c0)
// c4 = d1c1
z4 := e.fp.Mul(d1, c1)
// c5 = d1 + c1,
z5 := e.fp.Add(d1, c1)

return [5]*baseEl{z0, z2, z3, z4, z5}
}

// mul023By023 multiplies two E6 sparse element of the form:
//
// E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0}
//
// and
//
// E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0}
func (e Ext6) mul023By023(d0, d1, c0, c1 *baseEl) [5]*baseEl {
x0 := e.fp.Mul(c0, d0)
x1 := e.fp.Mul(c1, d1)
x04 := e.fp.Add(c0, d0)
Expand All @@ -224,9 +284,48 @@ func (e Ext6) Mul023By023(d0, d1, c0, c1 *baseEl) [5]*baseEl {

// MulBy02345 multiplies z by an E6 sparse element of the form
//
// E6{A0: y0, A1: 0, A2: y1, A3: y2, A4: y3, A5: y4},
// }
// E6{A0: y0, A1: 0, A2: y1, A3: y2, A4: y3, A5: y4}
func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 {
return e.mulBy02345Direct(z, x)
}

// mulBy02345Direct multiplies z by an E6 sparse element using schoolbook multiplication
func (e Ext6) mulBy02345Direct(z *E6, x [5]*baseEl) *E6 {
nonResidue := e.fp.NewElement(-4)

// c0 = a0y0 + β(a1y4 + a2y3 + a3y2 + a4y1)
c0 := e.fp.Eval([][]*baseEl{{&z.A0, x[0]}, {nonResidue, &z.A1, x[4]}, {nonResidue, &z.A2, x[3]}, {nonResidue, &z.A3, x[2]}, {nonResidue, &z.A4, x[1]}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c1 = a1y0 + β(a2y4 + a3y3 + a4y2 + a5y1)
c1 := e.fp.Eval([][]*baseEl{{&z.A1, x[0]}, {nonResidue, &z.A2, x[4]}, {nonResidue, &z.A3, x[3]}, {nonResidue, &z.A4, x[2]}, {nonResidue, &z.A5, x[1]}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c2 = a0y1 + a2y0 + β(a3y4 + a4y3 + a5y2)
c2 := e.fp.Eval([][]*baseEl{{&z.A0, x[1]}, {&z.A2, x[0]}, {nonResidue, &z.A3, x[4]}, {nonResidue, &z.A4, x[3]}, {nonResidue, &z.A5, x[2]}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c3 = a0y2 + a1y1 + a3y0 + β(a4y4 + a5y3)
c3 := e.fp.Eval([][]*baseEl{{&z.A0, x[2]}, {&z.A1, x[1]}, {&z.A3, x[0]}, {nonResidue, &z.A4, x[4]}, {nonResidue, &z.A5, x[3]}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c4 = a0y3 + a1y2 + a2y1 + a4y0 + βa5y4
c4 := e.fp.Eval([][]*baseEl{{&z.A0, x[3]}, {&z.A1, x[2]}, {&z.A2, x[1]}, {&z.A4, x[0]}, {nonResidue, &z.A5, x[4]}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
// c5 = a0y4 + a1y3 + a2y2 + a3y1 + a5y0,
c5 := e.fp.Eval([][]*baseEl{{&z.A0, x[4]}, {&z.A1, x[3]}, {&z.A2, x[2]}, {&z.A3, x[1]}, {&z.A5, x[0]}},
[]*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})

return &E6{
A0: *c0,
A1: *c1,
A2: *c2,
A3: *c3,
A4: *c4,
A5: *c5,
}
}

// mulBy02345 multiplies z by an E6 sparse element of the form
//
// E6{A0: y0, A1: 0, A2: y1, A3: y2, A4: y3, A5: y4},
func (e *Ext6) mulBy02345(z *E6, x [5]*baseEl) *E6 {
a0 := e.fp.Add(&z.A0, &z.A1)
a1 := e.fp.Add(&z.A2, &z.A3)
a2 := e.fp.Add(&z.A4, &z.A5)
Expand Down
Loading