gnark-plonky2-verifier/plonk/gates/coset_interpolation_gate.go
plonk/gates/coset_interpolation_gate.goRaw
1package gates
2
3import (
4 "fmt"
5 "regexp"
6 "strconv"
7 "strings"
8
9 "github.com/consensys/gnark-crypto/field/goldilocks"
10 "github.com/consensys/gnark/frontend"
11 gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks"
12)
13
14var cosetInterpolationGateRegex = regexp.MustCompile(`CosetInterpolationGate { subgroup_bits: (?P<subgroupBits>[0-9]+), degree: (?P<degree>[0-9]+), barycentric_weights: \[(?P<barycentricWeights>[0-9, ]+)\], _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>`)
15
16func deserializeCosetInterpolationGate(parameters map[string]string) Gate {
17 // Has the format CosetInterpolationGate { subgroup_bits: 4, degree: 6, barycentric_weights: [17293822565076172801, 18374686475376656385, 18446744069413535745, 281474976645120, 17592186044416, 18446744069414584577, 18446744000695107601, 18446744065119617025, 1152921504338411520, 72057594037927936, 18446744069415632897, 18446462594437939201, 18446726477228539905, 18446744069414584065, 68719476720, 4294967296], _phantom: PhantomData<plonky2_field::goldilocks_field::GoldilocksField> }<D=2>
18 subgroupBits, hasSubgroupBits := parameters["subgroupBits"]
19 degree, hasDegree := parameters["degree"]
20 barycentricWeights, hasBarycentricWeights := parameters["barycentricWeights"]
21
22 if !hasSubgroupBits || !hasDegree || !hasBarycentricWeights {
23 panic("missing subgroupBits, degree or barycentricWeights in CosetInterpolationGate")
24 }
25
26 subgroupBitsInt, err := strconv.ParseUint(subgroupBits, 10, 64)
27 if err != nil {
28 panic("invalid subgroupBits in CosetInterpolationGate")
29 }
30
31 degreeInt, err := strconv.ParseUint(degree, 10, 64)
32 if err != nil {
33 panic("invalid degree in CosetInterpolationGate")
34 }
35
36 barycentricWeightsStr := strings.Split(barycentricWeights, ",")
37 barycentricWeightsInt := make([]goldilocks.Element, len(barycentricWeightsStr))
38 for i, barycentricWeightStr := range barycentricWeightsStr {
39 barycentricWeightStr = strings.TrimSpace(barycentricWeightStr)
40 barycentricWeightInt, err := strconv.ParseUint(barycentricWeightStr, 10, 64)
41 if err != nil {
42 panic("invalid barycentricWeights in CosetInterpolationGate")
43 }
44 barycentricWeightsInt[i].SetUint64(barycentricWeightInt)
45 }
46
47 return NewCosetInterpolationGate(subgroupBitsInt, degreeInt, barycentricWeightsInt)
48}
49
50type CosetInterpolationGate struct {
51 subgroupBits uint64
52 degree uint64
53 barycentricWeights []goldilocks.Element
54}
55
56func NewCosetInterpolationGate(subgroupBits uint64, degree uint64, barycentricWeights []goldilocks.Element) *CosetInterpolationGate {
57 return &CosetInterpolationGate{
58 subgroupBits: subgroupBits,
59 degree: degree,
60 barycentricWeights: barycentricWeights,
61 }
62}
63
64func (g *CosetInterpolationGate) Id() string {
65
66 return fmt.Sprintf(
67 "CosetInterpolationGate { subgroup_bits: %d, degree: %d, barycentric_weights: %s }",
68 g.subgroupBits,
69 g.degree,
70 fmt.Sprint(g.barycentricWeights),
71 )
72}
73
74func (g *CosetInterpolationGate) numPoints() uint64 {
75 return 1 << g.subgroupBits
76}
77
78// Wire index of the coset shift.
79func (g *CosetInterpolationGate) wireShift() uint64 {
80 return 0
81}
82
83func (g *CosetInterpolationGate) startValues() uint64 {
84 return 1
85}
86
87// Wire indices of the `i`th interpolant value.
88func (g *CosetInterpolationGate) wiresValue(i uint64) Range {
89 if i >= g.numPoints() {
90 panic("Invalid point index")
91 }
92 start := g.startValues() + i*gl.D
93 return Range{start, start + gl.D}
94}
95
96func (g *CosetInterpolationGate) startEvaluationPoint() uint64 {
97 return g.startValues() + g.numPoints()*gl.D
98}
99
100// Wire indices of the point to evaluate the interpolant at.
101func (g *CosetInterpolationGate) wiresEvaluationPoint() Range {
102 start := g.startEvaluationPoint()
103 return Range{start, start + gl.D}
104}
105
106func (g *CosetInterpolationGate) startEvaluationValue() uint64 {
107 return g.startEvaluationPoint() + gl.D
108}
109
110// Wire indices of the interpolated value.
111func (g *CosetInterpolationGate) wiresEvaluationValue() Range {
112 start := g.startEvaluationValue()
113 return Range{start, start + gl.D}
114}
115
116func (g *CosetInterpolationGate) startIntermediates() uint64 {
117 return g.startEvaluationValue() + gl.D
118}
119
120func (g *CosetInterpolationGate) numIntermediates() uint64 {
121 return (g.numPoints() - 2) / (g.degree - 1)
122}
123
124// The wires corresponding to the i'th intermediate evaluation.
125func (g *CosetInterpolationGate) wiresIntermediateEval(i uint64) Range {
126 if i >= g.numIntermediates() {
127 panic("Invalid intermediate index")
128 }
129 start := g.startIntermediates() + gl.D*i
130 return Range{start, start + gl.D}
131}
132
133// The wires corresponding to the i'th intermediate product.
134func (g *CosetInterpolationGate) wiresIntermediateProd(i uint64) Range {
135 if i >= g.numIntermediates() {
136 panic("Invalid intermediate index")
137 }
138 start := g.startIntermediates() + gl.D*(g.numIntermediates()+i)
139 return Range{start, start + gl.D}
140}
141
142// Wire indices of the shifted point to evaluate the interpolant at.
143func (g *CosetInterpolationGate) wiresShiftedEvaluationPoint() Range {
144 start := g.startIntermediates() + gl.D*2*g.numIntermediates()
145 return Range{start, start + gl.D}
146}
147
148func (g *CosetInterpolationGate) EvalUnfiltered(
149 api frontend.API,
150 glApi gl.Chip,
151 vars EvaluationVars,
152) []gl.QuadraticExtensionVariable {
153 constraints := []gl.QuadraticExtensionVariable{}
154
155 shift := vars.localWires[g.wireShift()]
156 evaluationPoint := vars.GetLocalExtAlgebra(g.wiresEvaluationPoint())
157 shiftedEvaluationPoint := vars.GetLocalExtAlgebra(g.wiresShiftedEvaluationPoint())
158
159 negShift := glApi.ScalarMulExtension(shift, gl.NegOne())
160
161 tmp := glApi.ScalarMulExtensionAlgebra(negShift, shiftedEvaluationPoint)
162 tmp = glApi.AddExtensionAlgebra(tmp, evaluationPoint)
163
164 for i := 0; i < gl.D; i++ {
165 constraints = append(constraints, tmp[i])
166 }
167
168 domain := gl.TwoAdicSubgroup(g.subgroupBits)
169 values := []gl.QuadraticExtensionAlgebraVariable{}
170 for i := uint64(0); i < g.numPoints(); i++ {
171 values = append(values, vars.GetLocalExtAlgebra(g.wiresValue(i)))
172 }
173 weights := g.barycentricWeights
174
175 initialEval := gl.ZeroExtensionAlgebra()
176 initialProd := gl.OneExtensionAlgebra()
177 computedEval, computedProd := glApi.PartialInterpolateExtAlgebra(
178 domain[:g.degree],
179 values[:g.degree],
180 weights[:g.degree],
181 shiftedEvaluationPoint,
182 initialEval,
183 initialProd,
184 )
185
186 for i := uint64(0); i < g.numIntermediates(); i++ {
187 intermediateEval := vars.GetLocalExtAlgebra(g.wiresIntermediateEval(i))
188 intermediateProd := vars.GetLocalExtAlgebra(g.wiresIntermediateProd(i))
189
190 evalDiff := glApi.SubExtensionAlgebra(intermediateEval, computedEval)
191 for j := 0; j < gl.D; j++ {
192 constraints = append(constraints, evalDiff[j])
193 }
194
195 prodDiff := glApi.SubExtensionAlgebra(intermediateProd, computedProd)
196 for j := 0; j < gl.D; j++ {
197 constraints = append(constraints, prodDiff[j])
198 }
199
200 startIndex := 1 + (g.degree-1)*(i+1)
201 endIndex := startIndex + g.degree - 1
202 if endIndex > g.numPoints() {
203 endIndex = g.numPoints()
204 }
205
206 computedEval, computedProd = glApi.PartialInterpolateExtAlgebra(
207 domain[startIndex:endIndex],
208 values[startIndex:endIndex],
209 weights[startIndex:endIndex],
210 shiftedEvaluationPoint,
211 intermediateEval,
212 intermediateProd,
213 )
214 }
215
216 evaluationValue := vars.GetLocalExtAlgebra(g.wiresEvaluationValue())
217 evalDiff := glApi.SubExtensionAlgebra(evaluationValue, computedEval)
218 for j := 0; j < gl.D; j++ {
219 constraints = append(constraints, evalDiff[j])
220 }
221
222 return constraints
223}