Skip to content

Commit

Permalink
Adding polynomial multiplication based on NTT.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jan 28, 2025
1 parent 54f9a9e commit 9aa9bc4
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 75 deletions.
4 changes: 0 additions & 4 deletions vdaf/prio3/arith/arith.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,4 @@ type Poly[Poly ~[]E, E Elt] interface {
Evaluate(x *E) E
// Strip removes the higher-degree zero terms.
Strip() Poly
// Interpolate a polynomial passing through the points (x[i], y[i]), where
// x are the powers of an N-th root of unity, y are the values, and
// N = len(y) must be a power of two.
Interpolate(y []E)
}
35 changes: 27 additions & 8 deletions vdaf/prio3/arith/fp128/poly.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 9 additions & 12 deletions vdaf/prio3/arith/fp128/vector.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 27 additions & 8 deletions vdaf/prio3/arith/fp64/poly.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 9 additions & 12 deletions vdaf/prio3/arith/fp64/vector.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 42 additions & 10 deletions vdaf/prio3/arith/poly_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func testPoly[P Poly[P, E], V Vec[V, E], E EltTest, F Fp[E]](t *testing.T) {
t.Run("interpolate", interpolate[P, V, E, F])
t.Run("strip", strip[P, E, F])
t.Run("mulSqr", mulSqrPoly[P, V, E, F])
t.Run("mulNTT", mulNTT[P, V, E, F])
}

func mulSqrPoly[P Poly[P, E], V Vec[V, E], E EltTest, F Fp[E]](t *testing.T) {
Expand Down Expand Up @@ -48,10 +49,10 @@ func mulSqrPoly[P Poly[P, E], V Vec[V, E], E EltTest, F Fp[E]](t *testing.T) {
}
}

func evalRootsUnity[P Poly[P, E], V Vec[V, E], E Elt, F Fp[E]](p P) V {
func evalRootsUnity[P Poly[P, E], V Vec[V, E], E Elt, F Fp[E]](p P, n uint) V {
// evaluate p on the powers of the root of unity.
// p(w^0), p(w^1), p(w^2), ...
N, logN := math.NextPow2(uint(len(p)))
N, logN := math.NextPow2(n)
var wi, wn F = new(E), new(E)
wi.SetOne()
wn.SetRootOfUnityTwoN(logN)
Expand All @@ -71,16 +72,20 @@ func interpolate[P Poly[P, E], V Vec[V, E], E EltTest, F Fp[E]](t *testing.T) {
N := uint(1) << logN
p := NewPoly[P](N - 1)
mustRead(t, V(p))
values := evalRootsUnity[P, V, E, F](p)
values := evalRootsUnity[P, V, E, F](p, N)

y := NewVec[V](N)
y.NTT(V(p), N)
if !slices.Equal(y, values) {
test.ReportError(t, y, values)
}

var invN F = new(E)
invN.InvTwoN(uint(logN))

p2 := NewPoly[P](N - 1)
p2.Interpolate(values)
V(p2).InvNTT(values, N)
V(p2).ScalarMul(invN)
if !slices.Equal(p, p2) {
test.ReportError(t, p, p2)
}
Expand All @@ -101,6 +106,38 @@ func strip[P Poly[P, E], E Elt, F Fp[E]](t *testing.T) {
}
}

type polyTest[P any] interface {
MulNlogN(x, y P)
MulNSquare(x, y P)
}

func mulNTT[P Poly[P, E], V Vec[V, E], E EltTest, F Fp[E]](t *testing.T) {
const DegX uint = 16
const DegY uint = 16

for degX := range DegX {
for degY := range DegY {
degZ := degX + degY
x := NewPoly[P](degX)
y := NewPoly[P](degY)
got := NewPoly[P](degZ)
want := NewPoly[P](degZ)

mustRead(t, V(x))
mustRead(t, V(y))

any(got).(polyTest[P]).MulNlogN(x, y)
any(want).(polyTest[P]).MulNSquare(x, y)

if !slices.EqualFunc(got, want,
func(x, y E) bool { return F(&x).IsEqual(&y) },
) {
test.ReportError(t, got, want, degX, degY)
}
}
}
}

func benchmarkPoly[P Poly[P, E], V Vec[V, E], E Elt, F Fp[E]](b *testing.B) {
x := F(new(E))
p := NewPoly[P](Degree)
Expand All @@ -113,7 +150,7 @@ func benchmarkPoly[P Poly[P, E], V Vec[V, E], E Elt, F Fp[E]](b *testing.B) {
N, _ := math.NextPow2(Degree)
pol := NewPoly[P](N - 1)
mustRead(b, V(pol))
values := evalRootsUnity[P, V, E, F](pol)
values := evalRootsUnity[P, V, E, F](pol, N)

b.Run("AddAssign", func(b *testing.B) {
for i := 0; i < b.N; i++ {
Expand Down Expand Up @@ -145,9 +182,4 @@ func benchmarkPoly[P Poly[P, E], V Vec[V, E], E Elt, F Fp[E]](b *testing.B) {
V(p).InvNTT(values, N)
}
})
b.Run("Interpolate", func(b *testing.B) {
for i := 0; i < b.N; i++ {
pol.Interpolate(values)
}
})
}
34 changes: 27 additions & 7 deletions vdaf/prio3/arith/templates/poly.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ type Poly []Fp
func (p Poly) AddAssign(x Poly) { Vec(p).AddAssign(Vec(x)) }
func (p Poly) SubAssign(x Poly) { Vec(p).SubAssign(Vec(x)) }
func (p Poly) Mul(x, y Poly) {
const thresholdPolyMul = 128
if len(x) + len(y) - 1 < thresholdPolyMul {
p.MulNSquare(x, y)
} else{
p.MulNlogN(x, y)
}
}

func (p Poly) MulNSquare(x, y Poly) {
mustSumLen(p, x, y)
clear(p)
var xiyj Fp
Expand All @@ -24,6 +33,24 @@ func (p Poly) Mul(x, y Poly) {
}
}

func (p Poly) MulNlogN(x, y Poly) {
mustSumLen(p, x, y)
N, logN := math.NextPow2(uint(len(x) + len(y) - 1))
buf := make(Vec, 2*N)
lx, ly := buf[:N], buf[N:]
lx.NTT(Vec(x), N)
ly.NTT(Vec(y), N)
for i := range lx {
lx[i].MulAssign(&ly[i])
}

ly.InvNTT(lx, N)
var invN Fp
invN.InvTwoN(logN)
copy(p, ly)
Vec(p).ScalarMul(&invN)
}

func (p Poly) Sqr(x Poly) {
mustSumLen(p, x, x)
clear(p)
Expand Down Expand Up @@ -63,10 +90,3 @@ func (p Poly) Strip() Poly {
return p[:0]
}

func (p Poly) Interpolate(values []Fp) {
N, logN := math.NextPow2(uint(len(values)))
Vec(p).InvNTT(values, N)
var invN Fp
invN.InvTwoN(logN)
Vec(p).ScalarMul(&invN)
}
Loading

0 comments on commit 9aa9bc4

Please sign in to comment.