From 915cd4d3a7f7082c6504dc4945aacb0936edfabc Mon Sep 17 00:00:00 2001 From: Azam Soleimanian <49027816+Soleimani193@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:11:43 +0200 Subject: [PATCH] Prover/splitter compiler (#74) * ignore queries over the eligible columns * adding the constraints * fix the golangci-lint error * organizing the package * adding a missing verifier check * support shifted version of verifier columns * adding a test case for verifier columns * readding the old splitter --- .../column/verifiercol/expand_verifcol.go | 1 - .../splitter/stitcher/stitcher_test.go | 296 --------------- .../splitter => stitch_split}/common.go | 0 .../stitch_split/splitter/constraints.go | 353 ++++++++++++++++++ .../stitch_split/splitter/splitter.go | 160 ++++++++ .../stitch_split/splitter/splitter_test.go | 289 ++++++++++++++ .../stitcher/constraints.go | 64 +++- .../stitcher/stitcher.go | 44 ++- .../stitch_split/stitcher/stitcher_test.go | 322 ++++++++++++++++ prover/protocol/query/global.go | 3 + prover/protocol/query/global_test.go | 24 +- prover/zkevm/prover/hash/packing/cld.go | 5 +- .../hash/packing/dedicated/len_consistency.go | 7 +- 13 files changed, 1225 insertions(+), 343 deletions(-) delete mode 100644 prover/protocol/compiler/splitter/splitter/stitcher/stitcher_test.go rename prover/protocol/compiler/{splitter/splitter => stitch_split}/common.go (100%) create mode 100644 prover/protocol/compiler/stitch_split/splitter/constraints.go create mode 100644 prover/protocol/compiler/stitch_split/splitter/splitter.go create mode 100644 prover/protocol/compiler/stitch_split/splitter/splitter_test.go rename prover/protocol/compiler/{splitter/splitter => stitch_split}/stitcher/constraints.go (81%) rename prover/protocol/compiler/{splitter/splitter => stitch_split}/stitcher/stitcher.go (91%) create mode 100644 prover/protocol/compiler/stitch_split/stitcher/stitcher_test.go diff --git a/prover/protocol/column/verifiercol/expand_verifcol.go b/prover/protocol/column/verifiercol/expand_verifcol.go index b984cc3e5..a631f4041 100644 --- a/prover/protocol/column/verifiercol/expand_verifcol.go +++ b/prover/protocol/column/verifiercol/expand_verifcol.go @@ -52,7 +52,6 @@ func (ex ExpandedVerifCol) GetColAssignment(run ifaces.Runtime) ifaces.ColAssign // GetColAssignment returns a gnark assignment of the current column func (ex ExpandedVerifCol) GetColAssignmentGnark(run ifaces.GnarkRuntime) []frontend.Variable { - assi := ex.Verifiercol.GetColAssignmentGnark(run) res := make([]frontend.Variable, ex.Size()) for i := 0; i < len(assi); i++ { diff --git a/prover/protocol/compiler/splitter/splitter/stitcher/stitcher_test.go b/prover/protocol/compiler/splitter/splitter/stitcher/stitcher_test.go deleted file mode 100644 index e0534173e..000000000 --- a/prover/protocol/compiler/splitter/splitter/stitcher/stitcher_test.go +++ /dev/null @@ -1,296 +0,0 @@ -package stitcher_test - -import ( - "testing" - - "github.com/consensys/linea-monorepo/prover/maths/common/smartvectors" - "github.com/consensys/linea-monorepo/prover/maths/field" - "github.com/consensys/linea-monorepo/prover/protocol/accessors" - "github.com/consensys/linea-monorepo/prover/protocol/coin" - "github.com/consensys/linea-monorepo/prover/protocol/column" - "github.com/consensys/linea-monorepo/prover/protocol/column/verifiercol" - "github.com/consensys/linea-monorepo/prover/protocol/compiler/dummy" - "github.com/consensys/linea-monorepo/prover/protocol/compiler/splitter/splitter/stitcher" - "github.com/consensys/linea-monorepo/prover/protocol/ifaces" - "github.com/consensys/linea-monorepo/prover/protocol/query" - "github.com/consensys/linea-monorepo/prover/protocol/wizard" - "github.com/consensys/linea-monorepo/prover/symbolic" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestLocalEval(t *testing.T) { - - var a, b, c, d ifaces.Column - var q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12 query.LocalOpening - - define := func(builder *wizard.Builder) { - // declare columns of different sizes - a = builder.RegisterCommit("A", 2) - b = builder.RegisterCommit("B", 4) - c = builder.RegisterCommit("C", 8) - d = builder.RegisterCommit("D", 16) - - // Local opening at zero - q1 = builder.LocalOpening("Q00", a) - q2 = builder.LocalOpening("Q01", b) - q3 = builder.LocalOpening("Q02", c) - q4 = builder.LocalOpening("Q03", d) - - // Local opening at but shifted by one - q5 = builder.LocalOpening("Q10", column.Shift(a, 1)) - q6 = builder.LocalOpening("Q11", column.Shift(b, 1)) - q7 = builder.LocalOpening("Q12", column.Shift(c, 1)) - q8 = builder.LocalOpening("Q13", column.Shift(d, 1)) - - // Local opening at but shifted by one - q9 = builder.LocalOpening("Q20", column.Shift(a, -1)) - q10 = builder.LocalOpening("Q21", column.Shift(b, -1)) - q11 = builder.LocalOpening("Q22", column.Shift(c, -1)) - q12 = builder.LocalOpening("Q23", column.Shift(d, -1)) - } - - comp := wizard.Compile(define, stitcher.Stitcher(4, 8)) - - //after stitcing-compilation we expect that the eligible columns and their relevant queries be ignored - assert.Equal(t, column.Proof.String(), comp.Columns.Status("A").String()) - assert.Equal(t, column.Ignored.String(), comp.Columns.Status("B").String()) - assert.Equal(t, column.Committed.String(), comp.Columns.Status("C").String()) - assert.Equal(t, column.Committed.String(), comp.Columns.Status("D").String()) - - assert.Equal(t, true, comp.QueriesParams.IsIgnored(q1.ID)) - assert.Equal(t, true, comp.QueriesParams.IsIgnored(q2.ID)) - assert.Equal(t, false, comp.QueriesParams.IsIgnored(q3.ID)) - assert.Equal(t, false, comp.QueriesParams.IsIgnored(q4.ID)) - assert.Equal(t, true, comp.QueriesParams.IsIgnored(q5.ID)) - assert.Equal(t, true, comp.QueriesParams.IsIgnored(q6.ID)) - assert.Equal(t, false, comp.QueriesParams.IsIgnored(q7.ID)) - assert.Equal(t, false, comp.QueriesParams.IsIgnored(q8.ID)) - assert.Equal(t, true, comp.QueriesParams.IsIgnored(q9.ID)) - assert.Equal(t, true, comp.QueriesParams.IsIgnored(q10.ID)) - assert.Equal(t, false, comp.QueriesParams.IsIgnored(q11.ID)) - assert.Equal(t, false, comp.QueriesParams.IsIgnored(q12.ID)) - - // manually compiles the comp - dummy.Compile(comp) - - proof := wizard.Prove(comp, func(assi *wizard.ProverRuntime) { - // Assigns all the columns - assi.AssignColumn(a.GetColID(), smartvectors.ForTest(0, 1)) - assi.AssignColumn(b.GetColID(), smartvectors.ForTest(2, 3, 4, 5)) - assi.AssignColumn(c.GetColID(), smartvectors.ForTest(6, 7, 8, 9, 10, 11, 12, 13)) - assi.AssignColumn(d.GetColID(), smartvectors.ForTest(15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30)) - - // And the alleged results - assi.AssignLocalPoint("Q00", field.NewElement(0)) - assi.AssignLocalPoint("Q01", field.NewElement(2)) - assi.AssignLocalPoint("Q02", field.NewElement(6)) - assi.AssignLocalPoint("Q03", field.NewElement(15)) - assi.AssignLocalPoint("Q10", field.NewElement(1)) - assi.AssignLocalPoint("Q11", field.NewElement(3)) - assi.AssignLocalPoint("Q12", field.NewElement(7)) - assi.AssignLocalPoint("Q13", field.NewElement(16)) - assi.AssignLocalPoint("Q20", field.NewElement(1)) - assi.AssignLocalPoint("Q21", field.NewElement(5)) - assi.AssignLocalPoint("Q22", field.NewElement(13)) - assi.AssignLocalPoint("Q23", field.NewElement(30)) - }) - - err := wizard.Verify(comp, proof) - require.NoError(t, err) - -} - -func TestGlobalConstraintFibonacci(t *testing.T) { - - var a, b, c ifaces.Column - var q1, q2, q3 query.GlobalConstraint - - define := func(builder *wizard.Builder) { - // declare columns of different sizes - a = builder.RegisterCommit("B", 4) - // a = verifiercol.NewConstantCol(field.One(), 4) - b = builder.RegisterCommit("C", 8) - c = builder.RegisterCommit("D", 16) - - fibo := func(col ifaces.Column) *symbolic.Expression { - col_ := ifaces.ColumnAsVariable(col) - colNext := ifaces.ColumnAsVariable(column.Shift(col, 1)) - colNextNext := ifaces.ColumnAsVariable(column.Shift(col, 2)) - return colNextNext.Sub(colNext).Sub(col_) - } - - q1 = builder.GlobalConstraint("Q0", fibo(a)) - q2 = builder.GlobalConstraint("Q1", fibo(b)) - q3 = builder.GlobalConstraint("Q2", fibo(c)) - } - - comp := wizard.Compile(define, stitcher.Stitcher(8, 16)) - - //after stitcing-compilation we expect that the eligible columns and their relevant queries be ignored - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q1.ID), "q1 should be ignored") - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q2.ID), "q2 should not be ignored") - assert.Equal(t, false, comp.QueriesNoParams.IsIgnored(q3.ID), "q3 should not be ignored") - - // manually compiles the comp - dummy.Compile(comp) - - proof := wizard.Prove(comp, func(assi *wizard.ProverRuntime) { - // Assigns all the columns - assi.AssignColumn(a.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - assi.AssignColumn(b.GetColID(), smartvectors.ForTest(1, 1, 2, 3, 5, 8, 13, 21)) - assi.AssignColumn(c.GetColID(), smartvectors.ForTest(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987)) - }) - - err := wizard.Verify(comp, proof) - require.NoError(t, err) - -} - -func TestLocalConstraintFibonacci(t *testing.T) { - - var a, b, c ifaces.Column - var q1, q2, q3 query.LocalConstraint - - define := func(builder *wizard.Builder) { - // declare columns of different sizes - a = builder.RegisterCommit("B", 4) - b = builder.RegisterCommit("C", 8) - c = builder.RegisterCommit("D", 16) - - fibo := func(col ifaces.Column) *symbolic.Expression { - col_ := ifaces.ColumnAsVariable(col) - colNext := ifaces.ColumnAsVariable(column.Shift(col, 1)) - colNextNext := ifaces.ColumnAsVariable(column.Shift(col, 2)) - return colNextNext.Sub(colNext).Sub(col_) - } - - q1 = builder.LocalConstraint("Q0", fibo(a)) - q2 = builder.LocalConstraint("Q1", fibo(b)) - q3 = builder.LocalConstraint("Q2", fibo(c)) - } - - comp := wizard.Compile(define, stitcher.Stitcher(8, 16)) - - //after stitcing-compilation we expect that the eligible columns and their relevant queries be ignored - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q1.ID), "q1 should be ignored") - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q2.ID), "q2 should not be ignored") - assert.Equal(t, false, comp.QueriesNoParams.IsIgnored(q3.ID), "q3 should not be ignored") - - // manually compiles the comp - dummy.Compile(comp) - - proof := wizard.Prove(comp, func(assi *wizard.ProverRuntime) { - // Assigns all the columns - // Todo: Arbitrary changes of col values do not make the test failing - assi.AssignColumn(a.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - assi.AssignColumn(b.GetColID(), smartvectors.ForTest(1, 1, 2, 3, 5, 8, 13, 21)) - assi.AssignColumn(c.GetColID(), smartvectors.ForTest(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987)) - }) - - err := wizard.Verify(comp, proof) - require.NoError(t, err) - -} - -func TestGlobalMixedRounds(t *testing.T) { - - var a0, a1, a2, b0, b1, b2 ifaces.Column - var q0, q1, q2 query.LocalConstraint - - define := func(builder *wizard.Builder) { - // declare columns of different sizes - a0 = builder.RegisterCommit("A0", 4) - a1 = builder.RegisterCommit("A1", 4) - a2 = builder.RegisterCommit("A2", 4) - _ = builder.RegisterRandomCoin("COIN", coin.Field) - b0 = builder.RegisterCommit("B0", 4) - b1 = builder.RegisterCommit("B1", 4) - b2 = builder.RegisterCommit("B2", 4) - - q0 = builder.LocalConstraint("Q0", ifaces.ColumnAsVariable(a0).Sub(ifaces.ColumnAsVariable(b0))) - q1 = builder.LocalConstraint("Q1", ifaces.ColumnAsVariable(a1).Sub(ifaces.ColumnAsVariable(b1))) - q2 = builder.LocalConstraint("Q2", ifaces.ColumnAsVariable(a2).Sub(ifaces.ColumnAsVariable(b2))) - } - - comp := wizard.Compile(define, stitcher.Stitcher(4, 8)) - - //after stitcing-compilation we expect that the eligible columns and their relevant queries be ignored - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q0.ID), "q0 should be ignored") - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q1.ID), "q1 should be ignored") - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q2.ID), "q2 should be ignored") - - // manually compiles the comp - dummy.Compile(comp) - - proof := wizard.Prove(comp, func(assi *wizard.ProverRuntime) { - // Assigns all the columns - assi.AssignColumn(a0.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - assi.AssignColumn(a1.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - assi.AssignColumn(a2.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - _ = assi.GetRandomCoinField("COIN") // triggers going to the next round - assi.AssignColumn(b0.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - assi.AssignColumn(b1.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - assi.AssignColumn(b2.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - }) - - err := wizard.Verify(comp, proof) - require.NoError(t, err) -} - -func TestWithVerifCol(t *testing.T) { - var a, b, c, verifcol1, verifcol2 ifaces.Column - var q1, q2 query.GlobalConstraint - var q3 query.LocalConstraint - - define := func(builder *wizard.Builder) { - // declare columns of different sizes - a = builder.RegisterCommit("B", 4) - b = builder.RegisterCommit("C", 4) - // a new round - _ = builder.RegisterRandomCoin("COIN", coin.Field) - c = builder.RegisterCommit("D", 4) - // verifiercols - verifcol1 = verifiercol.NewConstantCol(field.NewElement(3), 4) - accessors := genAccessors([]int{1, 7, 5, 3}) - verifcol2 = verifiercol.NewFromAccessors(accessors, field.Zero(), 4) - - expr := symbolic.Sub(symbolic.Mul(a, verifcol1), b) - q1 = builder.GlobalConstraint("Q0", expr) - - expr = symbolic.Sub(symbolic.Add(a, verifcol2), c) - q2 = builder.GlobalConstraint("Q1", expr) - - q3 = builder.LocalConstraint("Q2", expr) - } - - comp := wizard.Compile(define, stitcher.Stitcher(4, 16)) - - //after stitcing-compilation we expect that the eligible columns and their relevant queries be ignored - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q1.ID), "q1 should be ignored") - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q2.ID), "q2 should be ignored") - assert.Equal(t, true, comp.QueriesNoParams.IsIgnored(q3.ID), "q2 should be ignored") - - // manually compiles the comp - dummy.Compile(comp) - - proof := wizard.Prove(comp, func(assi *wizard.ProverRuntime) { - // Assigns all the columns - assi.AssignColumn(a.GetColID(), smartvectors.ForTest(1, 1, 2, 3)) - assi.AssignColumn(b.GetColID(), smartvectors.ForTest(3, 3, 6, 9)) - _ = assi.GetRandomCoinField("COIN") // triggers going to the next round - assi.AssignColumn(c.GetColID(), smartvectors.ForTest(2, 8, 7, 6)) - }) - - err := wizard.Verify(comp, proof) - require.NoError(t, err) - -} - -func genAccessors(a []int) (res []ifaces.Accessor) { - for i := range a { - t := accessors.NewConstant(field.NewElement(uint64(a[i]))) - res = append(res, t) - } - return res -} diff --git a/prover/protocol/compiler/splitter/splitter/common.go b/prover/protocol/compiler/stitch_split/common.go similarity index 100% rename from prover/protocol/compiler/splitter/splitter/common.go rename to prover/protocol/compiler/stitch_split/common.go diff --git a/prover/protocol/compiler/stitch_split/splitter/constraints.go b/prover/protocol/compiler/stitch_split/splitter/constraints.go new file mode 100644 index 000000000..7eb3787eb --- /dev/null +++ b/prover/protocol/compiler/stitch_split/splitter/constraints.go @@ -0,0 +1,353 @@ +package splitter + +import ( + "reflect" + + "github.com/consensys/linea-monorepo/prover/protocol/coin" + "github.com/consensys/linea-monorepo/prover/protocol/column" + "github.com/consensys/linea-monorepo/prover/protocol/column/verifiercol" + alliance "github.com/consensys/linea-monorepo/prover/protocol/compiler/stitch_split" + "github.com/consensys/linea-monorepo/prover/protocol/ifaces" + "github.com/consensys/linea-monorepo/prover/protocol/query" + "github.com/consensys/linea-monorepo/prover/protocol/variables" + "github.com/consensys/linea-monorepo/prover/protocol/wizard" + "github.com/consensys/linea-monorepo/prover/symbolic" + "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/collection" +) + +func (ctx splitterContext) constraints() { + ctx.LocalOpening() + ctx.LocalGlobalConstraints() +} + +func (ctx splitterContext) LocalOpening() { + for _, qName := range ctx.comp.QueriesParams.AllUnignoredKeys() { + // Filters out only the LocalOpening + q, ok := ctx.comp.QueriesParams.Data(qName).(query.LocalOpening) + if !ok { + utils.Panic("got an uncompilable query %v", qName) + } + + round := ctx.comp.QueriesParams.Round(q.ID) + + if !isColEligible(ctx.Splittings, q.Pol) { + continue + } + // mark the query as ignored + ctx.comp.QueriesParams.MarkAsIgnored(qName) + // Get the sub column + subCol := getSubColForLocal(ctx, q.Pol, 0) + // apply the local constrain over the subCol + newQ := ctx.comp.InsertLocalOpening(round, queryName(q.ID), subCol) + + // Registers the prover's step responsible for assigning the new query + ctx.comp.SubProvers.AppendToInner(round, func(run *wizard.ProverRuntime) { + y := run.QueriesParams.MustGet(q.ID).(query.LocalOpeningParams).Y + run.AssignLocalPoint(newQ.ID, y) + }) + + } +} + +func (ctx splitterContext) LocalGlobalConstraints() { + for _, qName := range ctx.comp.QueriesNoParams.AllUnignoredKeys() { + + q := ctx.comp.QueriesNoParams.Data(qName) + // round of definition of the query to compile + round := ctx.comp.QueriesNoParams.Round(qName) + + var board symbolic.ExpressionBoard + + switch q := q.(type) { + case query.LocalConstraint: + board = q.Board() + // detect if the expression is eligible; + // i.e., it contains columns of proper size with status Precomputed, committed, or verifiercol. + if !alliance.IsExprEligible(isColEligible, ctx.Splittings, board) { + continue + } + + // if the associated expression is eligible to the stitching, mark the query, over the sub columns, as ignored. + ctx.comp.QueriesNoParams.MarkAsIgnored(qName) + + // adjust the query over the sub columns + ctx.comp.InsertLocal(round, queryName(qName), ctx.adjustExpressionForLocal(q.Expression, 0)) + + case query.GlobalConstraint: + board = q.Board() + // detect if the expression is over the eligible columns. + if !alliance.IsExprEligible(isColEligible, ctx.Splittings, board) { + continue + } + + // if the associated expression is eligible to the stitching, mark the query, over the sub columns, as ignored. + ctx.comp.QueriesNoParams.MarkAsIgnored(qName) + + // adjust the query over the sub columns + numSlots := q.DomainSize / ctx.size + for slot := 0; slot < numSlots; slot++ { + + ctx.comp.InsertGlobal(round, + ifaces.QueryIDf("%v_SPLITTER_GLOBALQ_SLOT_%v", q.ID, slot), + ctx.adjustExpressionForGlobal(q.Expression, slot), + ) + + ctx.localQueriesForGapsInGlobal(q, slot, numSlots) + } + + default: + utils.Panic("got an uncompilable query %++v", qName) + } + } +} + +// it checks if a column registered in the compiler has the proper size and state for splitting. +func isColEligible(splittings alliance.MultiSummary, col ifaces.Column) bool { + natural := column.RootParents(col)[0] + _, found := splittings[col.Round()].ByBigCol[natural.GetColID()] + return found +} + +// It finds the subCol containing the first row of col, +// it then shifts the subCol so that its first row equals with the first row of col. +func getSubColForLocal(ctx splitterContext, col ifaces.Column, posInCol int) ifaces.Column { + round := col.Round() + // Sanity-check : only for the edge-case h.Size() < ctx.size + if col.Size() < ctx.size && posInCol != 0 { + utils.Panic("We have h.Size (%v) < ctx.size (%v) but num (%v) != 0 for %v", col.Size(), ctx.size, posInCol, col.GetColID()) + } + + if !col.IsComposite() { + switch col := col.(type) { + case verifiercol.VerifierCol: + // Create the split in live + return col.Split(ctx.comp, posInCol*ctx.size, (posInCol+1)*ctx.size) + default: + // No changes : it means this is a normal column and + // we shall take the corresponding slice. + return ctx.Splittings[round].ByBigCol[col.GetColID()][posInCol] + } + } + + switch inner := col.(type) { + case column.Shifted: + // Shift the subparent, if the offset is larger than the subparent + // we repercute it on the num + if inner.Offset < -ctx.size { + utils.Panic("unsupported, the offset is too negative") + } + + // find the subCol that contain the first row of col + position := utils.PositiveMod(column.StackOffsets(col), col.Size()) + subColID, offsetOfSubCol := position/ctx.size, position%ctx.size + + // The subCol is linked to the "root" of q.Pol (i.e., natural column associated with col) + parent := getSubColForLocal(ctx, inner.Parent, posInCol+subColID) + return column.Shift(parent, offsetOfSubCol) + + default: + utils.Panic("unexpected type %v", reflect.TypeOf(inner)) + } + panic("unreachable") +} + +// For the column 'col' and the given 'posInCol', +// it returns the subColumn from the natural column located in position 'posInNatural'. +// where the posInNatural is calculated via the offset in Col. +func getSubColForGlobal(ctx splitterContext, col ifaces.Column, posInCol int) ifaces.Column { + // Sanity-check : only for the edge-case h.Size() < ctx.size + round := col.Round() + if col.Size() < ctx.size { + if posInCol > 0 { + utils.Panic( + "tried to get share #%v of column %v, but this is an undersized column %v", + posInCol, col.GetColID(), col.Size(), + ) + } + + // Not a split column : returns the input directly + return col + } + + if !col.IsComposite() { + switch m := col.(type) { + case verifiercol.VerifierCol: + // Create the split in live + return m.Split(ctx.comp, posInCol*ctx.size, (posInCol+1)*ctx.size) + default: + // No changes; natural column + return ctx.Splittings[round].ByBigCol[col.GetColID()][posInCol] + } + } + + switch inner := col.(type) { + // Shift the subparent, if the offset is larger than the subparent + // we repercute it on the num + case column.Shifted: + + // This works fine assuming h.Size() > ctx.size + var ( + offset = inner.Offset + maxNumSubCol = col.Size() / ctx.size + subColID = (posInCol + (offset / ctx.size)) % maxNumSubCol + offsetOfSubCol = utils.PositiveMod(offset, ctx.size) + ) + // This indicates that the offset is so large + if subColID < 0 { + subColID = (col.Size() / ctx.size) + subColID + } + // The resulting offset should keep the same sign as the old one. This is + // because the sign indicates which range of position is touched by + // bound cancellation. + if offsetOfSubCol*offset < 0 { + offsetOfSubCol -= ctx.size + } + parent := getSubColForGlobal(ctx, inner.Parent, subColID) + return column.Shift(parent, offsetOfSubCol) + + default: + utils.Panic("unexpected type %v", reflect.TypeOf(inner)) + } + panic("unreachable") +} + +func queryName(oldQ ifaces.QueryID) ifaces.QueryID { + return ifaces.QueryIDf("%v_SPLITTER", oldQ) +} + +// it shift all the columns inside the expression by shift and then applies the local constraints. +func (ctx splitterContext) adjustExpressionForLocal( + expr *symbolic.Expression, shift int, +) *symbolic.Expression { + + board := expr.Board() + metadatas := board.ListVariableMetadata() + translationMap := collection.NewMapping[string, *symbolic.Expression]() + + for _, metadata := range metadatas { + // Replace the expression by the one + + switch m := metadata.(type) { + case ifaces.Column: + subCol := getSubColForLocal(ctx, column.Shift(m, shift), 0) + translationMap.InsertNew(m.String(), ifaces.ColumnAsVariable(subCol)) + // @Azam why we need these cases? + case coin.Info, ifaces.Accessor: + translationMap.InsertNew(m.String(), symbolic.NewVariable(m)) + case variables.X: + panic("unsupported, the value of `x` in the unsplit query and the split would be different") + case variables.PeriodicSample: + // for PeriodicSampling offset is always positive in forward direction, thus we need (-shift) + newSample := variables.NewPeriodicSample(m.T, utils.PositiveMod(m.Offset-shift, m.T)) + translationMap.InsertNew(m.String(), newSample) + default: + // Repass the same variable + translationMap.InsertNew(m.String(), symbolic.NewVariable(metadata)) + } + } + + newExpr := expr.Replay(translationMap) + return newExpr +} + +func (ctx splitterContext) adjustExpressionForGlobal( + expr *symbolic.Expression, slot int, +) *symbolic.Expression { + board := expr.Board() + metadatas := board.ListVariableMetadata() + translationMap := collection.NewMapping[string, *symbolic.Expression]() + + for _, metadata := range metadatas { + + // For each slot, get the expression obtained by replacing the commitment + // by the appropriated column. + + switch m := metadata.(type) { + case ifaces.Column: + // Pass the same variable + subCol := getSubColForGlobal(ctx, m, slot) + // Sanity-check : the subHandle should have the target size + if subCol.Size() != ctx.size { + utils.Panic( + "outgoing column %v should have size %v but has size %v (ingoing column was %v, with size %v)", + subCol.GetColID(), ctx.size, subCol.Size(), m.GetColID(), m.Size(), + ) + } + translationMap.InsertNew(m.String(), ifaces.ColumnAsVariable(subCol)) + case variables.X: + utils.Panic("unsupported, the value of `x` in the unsplit query and the split would be different") + case variables.PeriodicSample: + // Check that the period is not larger than the domain size. If + // the period is smaller this is a no-op because the period does + // not change. + translated := symbolic.NewVariable(metadata) + + if m.T > ctx.size { + + // Here, there are two possibilities. (1) The current slot is + // on a portion of the Periodic sample where everything is + // zero or (2) the current slot matchs a portion of the + // periodic sampling containing a 1. To determine which is + // the current situation, we need to find out where the slot + // is located compared to the period. + var ( + slotStartAt = (slot * ctx.size) % m.T + slotStopAt = slotStartAt + ctx.size + ) + + if m.Offset >= slotStartAt && m.Offset < slotStopAt { + translated = variables.NewPeriodicSample(ctx.size, m.Offset%ctx.size) + } else { + translated = symbolic.NewConstant(0) + } + } + + // And we can just pass it over because the period does not change + translationMap.InsertNew(m.String(), translated) + default: + // Repass the same variable (for coins or other types of single-valued variable) + translationMap.InsertNew(m.String(), symbolic.NewVariable(metadata)) + } + + } + return expr.Replay(translationMap) +} + +func (ctx splitterContext) localQueriesForGapsInGlobal(q query.GlobalConstraint, slot, numSlots int) { + + // Now, we need to cancel the expression at the beginning and/or the end + // For the first one, only cancel the end. For the last one, only cancel + // the beginning. + offsetRange := q.MinMaxOffset() + round := ctx.comp.QueriesNoParams.Round(q.ID) + nextStart := 0 + + if offsetRange.Min < 0 { + for i := 0; i < offsetRange.Min; i-- { + // And fill the gap with a local constraint + if slot > 0 || q.NoBoundCancel { + // adjust the query over the sub columns + ctx.comp.InsertLocal(round, + ifaces.QueryIDf("%v_LOCAL_GAPS_NEG_OFFSET_%v", q.ID, i), + ctx.adjustExpressionForLocal(q.Expression, slot*ctx.size-i)) + } + } + if offsetRange.Max > 0 { + nextStart = 1 + } + } + + if offsetRange.Max > 0 { + for i := nextStart; i < offsetRange.Max; i++ { + point := ctx.size - i - 1 // point at which we want to cancel the constraint + // And fill the gap with a local constraint + if slot < numSlots-1 || q.NoBoundCancel { + shift := slot*ctx.size + point + ctx.comp.InsertLocal(round, + ifaces.QueryIDf("%v_LOCAL_GAPS_POS_OFFSET_%v_%v", q.ID, slot, i), + ctx.adjustExpressionForLocal(q.Expression, shift)) + } + } + } +} diff --git a/prover/protocol/compiler/stitch_split/splitter/splitter.go b/prover/protocol/compiler/stitch_split/splitter/splitter.go new file mode 100644 index 000000000..e3da14acb --- /dev/null +++ b/prover/protocol/compiler/stitch_split/splitter/splitter.go @@ -0,0 +1,160 @@ +package splitter + +import ( + "github.com/consensys/linea-monorepo/prover/protocol/column" + alliance "github.com/consensys/linea-monorepo/prover/protocol/compiler/stitch_split" + "github.com/consensys/linea-monorepo/prover/protocol/ifaces" + "github.com/consensys/linea-monorepo/prover/protocol/wizard" + "github.com/consensys/linea-monorepo/prover/utils" + "github.com/consensys/linea-monorepo/prover/utils/profiling" +) + +// Splitter +func Splitter(size int) func(*wizard.CompiledIOP) { + return func(comp *wizard.CompiledIOP) { + // it creates the splitting columns (from the eligible columns), and commits to them + ctx := newSplitter(comp, size) + // adjust the constraints accordingly over the stitchings of the sub columns. + ctx.constraints() + + // it assigns the stitching columns and delete the assignment of the sub columns. + comp.SubProvers.AppendToInner(comp.NumRounds()-1, func(run *wizard.ProverRuntime) { + for round := range ctx.Splittings { + for bigCol := range ctx.Splittings[round].ByBigCol { + run.Columns.TryDel(bigCol) + } + } + }) + } +} + +type splitterContext struct { + // the compiled IOP + comp *wizard.CompiledIOP + // the size for splitting the big columns + size int + // It collects the information about the splitting and subColumns. + // The index of Splittings is over the rounds. + Splittings []alliance.SummerizedAlliances +} + +func newSplitter(comp *wizard.CompiledIOP, size int) splitterContext { + numRound := comp.NumRounds() + ctx := splitterContext{ + comp: comp, + size: size, + Splittings: make([]alliance.SummerizedAlliances, numRound), + } + + ctx.ScanSplitCommit() + return ctx +} + +func (ctx *splitterContext) ScanSplitCommit() { + comp := ctx.comp + for round := 0; round < comp.NumRounds(); round++ { + for _, col := range comp.Columns.AllHandlesAtRound(round) { + + status := comp.Columns.Status(col.GetColID()) + + // 1. we expect no constraint over a mix of eligible columns and proof, thus ignore Proof columns + // 2. we expect no verifingKey column to fall withing the stitching interval (ctx.MinSize, ctx.MaxSize) + // 3. we expect no query over the ignored columns. + if status == column.Ignored || status == column.Proof || status == column.VerifyingKey { + continue + } + if col.Size() < ctx.size { + utils.Panic("stitcher is not working correctly, the small columns should have been handled by the stitcher") + } + + if col.Size()%ctx.size != 0 { + utils.Panic("the column size %v does not divide the splitting size %v", col.Size(), ctx.size) + } + if col.Size() == ctx.size { + continue + } + + // Create the subslices and give them the same status as their parents + numSubSlices := col.Size() / ctx.size + subSlices := make([]ifaces.Column, numSubSlices) + + // Note that the status of Verifiercol is not available via compiler. + // Thus, it is not handles here, we rather handle it during the constraints. + switch status { + // if the veriferKey is big, it should be declared as precomputed + case column.Precomputed, column.VerifyingKey: + // Then, on top of defining the new split column. We need to assign it + // directly. + precomp := comp.Precomputed.MustGet(col.GetColID()) + for i := 0; i < len(subSlices); i++ { + // InsertPrecomputed() automatically assigns the + // status "Precomputed". + subSlices[i] = comp.InsertPrecomputed( + nameHandleSlice(col, i, col.Size()/ctx.size), + precomp.SubVector(i*ctx.size, (i+1)*ctx.size), + ) + // For the verifyingKey, declare the status manually. + if status != column.Precomputed { + comp.Columns.SetStatus(subSlices[i].GetColID(), status) + } + } + + case column.Committed: + for i := 0; i < len(subSlices); i++ { + subSlices[i] = comp.InsertCommit(round, + nameHandleSlice(col, i, col.Size()/ctx.size), + ctx.size, + ) + } + default: + panic("Invalid Status") + } + + splitting := alliance.Alliance{ + BigCol: col, + SubCols: subSlices, + Round: round, + Status: status, + } + + (alliance.MultiSummary)(ctx.Splittings).InsertNew(splitting) + + // Mark the handle as ignored + comp.Columns.MarkAsIgnored(col.GetColID()) + } + ctx.comp.SubProvers.AppendToInner(round, ctx.Prove(round)) + } + +} + +func nameHandleSlice(h ifaces.Column, num, numSlots int) ifaces.ColID { + return ifaces.ColIDf("%v_SUBSLICE_%v_OVER_%v", h.GetColID(), num, numSlots) +} + +func (ctx splitterContext) Prove(round int) wizard.ProverStep { + + return func(run *wizard.ProverRuntime) { + stopTimer := profiling.LogTimer("splitter compiler") + defer stopTimer() + + for idBigCol, subCols := range ctx.Splittings[round].ByBigCol { + + // Sanity-check + bigCol := ctx.comp.Columns.GetHandle(idBigCol) + if len(subCols)*ctx.size != bigCol.Size() { + utils.Panic("Unexpected sizes %v * %v != %v", len(subCols), ctx.size, bigCol.Size()) + } + + // If the column is precomputed, it was already assigned + if ctx.comp.Precomputed.Exists(idBigCol) { + continue + } + + // assign the subColumns + witness := bigCol.GetColAssignment(run) + for i := 0; i < len(subCols); i++ { + run.AssignColumn(subCols[i].GetColID(), witness.SubVector(i*ctx.size, (i+1)*ctx.size)) + } + } + } +} diff --git a/prover/protocol/compiler/stitch_split/splitter/splitter_test.go b/prover/protocol/compiler/stitch_split/splitter/splitter_test.go new file mode 100644 index 000000000..70b65a953 --- /dev/null +++ b/prover/protocol/compiler/stitch_split/splitter/splitter_test.go @@ -0,0 +1,289 @@ +package splitter + +import ( + "testing" + + "github.com/consensys/linea-monorepo/prover/maths/common/smartvectors" + "github.com/consensys/linea-monorepo/prover/maths/common/vector" + "github.com/consensys/linea-monorepo/prover/maths/field" + "github.com/consensys/linea-monorepo/prover/protocol/accessors" + "github.com/consensys/linea-monorepo/prover/protocol/column" + "github.com/consensys/linea-monorepo/prover/protocol/column/verifiercol" + "github.com/consensys/linea-monorepo/prover/protocol/compiler/dummy" + "github.com/consensys/linea-monorepo/prover/protocol/compiler/stitch_split/stitcher" + "github.com/consensys/linea-monorepo/prover/protocol/ifaces" + "github.com/consensys/linea-monorepo/prover/protocol/query" + "github.com/consensys/linea-monorepo/prover/protocol/variables" + "github.com/consensys/linea-monorepo/prover/protocol/wizard" + "github.com/consensys/linea-monorepo/prover/symbolic" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + P1 ifaces.ColID = "P1" + GLOBAL1 ifaces.QueryID = "GLOBAL1" + LOCAL1 ifaces.QueryID = "LOCAL1" +) + +func TestSplitterWithFixedPointOpening(t *testing.T) { + testSplitter(t, 16, fixedPointOpening) +} + +func TestSplitterFibo(t *testing.T) { + testSplitter(t, 16, singlePolyFibo(4)) + testSplitter(t, 4, singlePolyFibo(8)) + testSplitter(t, 4, singlePolyFibo(16)) +} + +func TestSplitterGlobalWithPeriodicSample(t *testing.T) { + testSplitter(t, 64, globalWithPeriodicSample(16, 8, 0)) + testSplitter(t, 64, globalWithPeriodicSample(256, 8, 1)) + testSplitter(t, 64, globalWithPeriodicSample(256, 8, 7)) +} + +func TestSplitterLocalWithPeriodicSample(t *testing.T) { + testSplitter(t, 64, localWithPeriodicSample(256, 8, 0)) + testSplitter(t, 64, localWithPeriodicSample(256, 8, 1)) + testSplitter(t, 64, localWithPeriodicSample(256, 8, 7)) +} + +func fixedPointOpening() (wizard.DefineFunc, wizard.ProverStep) { + n := 1 << 6 + definer := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, n) + _ = build.LocalOpening("O1", P1) + _ = build.LocalOpening("O2", column.Shift(P1, 3)) + _ = build.LocalOpening("O3", column.Shift(P1, 4)) + _ = build.LocalOpening("O4", column.Shift(P1, -1)) + } + + prover := func(run *wizard.ProverRuntime) { + p1_ := make([]field.Element, n) + for i := range p1_ { + p1_[i].SetUint64(uint64(i)) + } + p1 := smartvectors.NewRegular(p1_) + run.AssignColumn(P1, p1) + run.AssignLocalPoint("O1", p1.Get(0)) + run.AssignLocalPoint("O2", p1.Get(3)) + run.AssignLocalPoint("O3", p1.Get(4)) + run.AssignLocalPoint("O4", p1.Get(n-1)) + } + + return definer, prover +} + +func singlePolyFibo(size int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + builder := func(build *wizard.Builder) { + // Number of rows + P1 := build.RegisterCommit(P1, size) // overshadows P + + // P(X) = P(X/w) + P(X/w^2) + expr := ifaces.ColumnAsVariable(column.Shift(P1, -1)). + Add(ifaces.ColumnAsVariable(column.Shift(P1, -2))). + Sub(ifaces.ColumnAsVariable(P1)) + + _ = build.GlobalConstraint(GLOBAL1, expr) + _ = build.LocalConstraint(LOCAL1, ifaces.ColumnAsVariable(P1).Sub(symbolic.NewConstant(1))) + } + + prover := func(run *wizard.ProverRuntime) { + x := make([]field.Element, size) + x[0].SetOne() + x[1].SetOne() + for i := 2; i < size; i++ { + x[i].Add(&x[i-1], &x[i-2]) + } + run.AssignColumn(P1, smartvectors.NewRegular(x)) + } + + return builder, prover + } +} + +func globalWithPeriodicSample(size, period, offset int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + + builder := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, size) // overshadows P + _ = build.GlobalConstraint(GLOBAL1, variables.NewPeriodicSample(period, offset).Mul(ifaces.ColumnAsVariable(P1))) + } + + prover := func(run *wizard.ProverRuntime) { + v := vector.Repeat(field.One(), size) + for i := 0; i < size; i++ { + if i%period == offset { + v[i].SetZero() + } + } + run.AssignColumn(P1, smartvectors.NewRegular(v)) + } + + return builder, prover + } +} + +func localWithPeriodicSample(size, period, offset int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + + builder := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, size) // overshadows P + _ = build.LocalConstraint(GLOBAL1, variables.NewPeriodicSample(period, offset).Mul(ifaces.ColumnAsVariable(P1))) + } + + prover := func(run *wizard.ProverRuntime) { + v := vector.Repeat(field.One(), size) + for i := 0; i < size; i++ { + if i%period == offset { + v[i].SetZero() + } + } + run.AssignColumn(P1, smartvectors.NewRegular(v)) + } + + return builder, prover + } +} + +func testSplitter(t *testing.T, splitSize int, gen func() (wizard.DefineFunc, wizard.ProverStep)) { + + // Activates the logs for easy debugging + logrus.SetLevel(logrus.TraceLevel) + + builder, prover := gen() + comp := wizard.Compile(builder, stitcher.Stitcher(splitSize/2, splitSize), Splitter(splitSize), dummy.Compile) + proof := wizard.Prove(comp, prover) + err := wizard.Verify(comp, proof) + + require.NoError(t, err) + + for _, qName := range comp.QueriesNoParams.AllKeysAt(0) { + + switch q := comp.QueriesNoParams.Data(qName).(type) { + + case query.GlobalConstraint: + board := q.Expression.Board() + metadatas := board.ListVariableMetadata() + metadataNames := []string{} + for i := range metadatas { + metadataNames = append(metadataNames, metadatas[i].String()) + } + t.Logf("query %v - with metadata %v", q.ID, metadataNames) + case query.LocalConstraint: + board := q.Expression.Board() + metadatas := board.ListVariableMetadata() + metadataNames := []string{} + for i := range metadatas { + metadataNames = append(metadataNames, metadatas[i].String()) + } + t.Logf("query %v - with metadata %v", q.ID, metadataNames) + } + } +} + +func globalWithVerifColAndPeriodic(size, period, offset int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + + builder := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, size) + verifcol1 := verifiercol.NewFromAccessors(genAccessors(0, size), field.Zero(), size) + verifcol2 := verifiercol.NewFromAccessors(genAccessors(2, size), field.Zero(), size) + _ = build.GlobalConstraint(LOCAL1, + symbolic.Sub( + + symbolic.Mul(symbolic.Sub(1, P1), + verifcol2), + + symbolic.Mul(variables.NewPeriodicSample(period, offset), + symbolic.Add(2, verifcol1))), + ) + } + + prover := func(run *wizard.ProverRuntime) { + v := vector.Repeat(field.One(), size) + for i := 0; i < size; i++ { + if i%period == offset { + v[i].SetZero() + } + } + run.AssignColumn(P1, smartvectors.NewRegular(v)) + } + + return builder, prover + } +} + +func TestLocalEvalWithStatus(t *testing.T) { + + var b, c ifaces.Column + var q2, q3, q6, q7, q10, q11 query.LocalOpening + + define := func(builder *wizard.Builder) { + // declare columns of different sizes + b = builder.RegisterCommit("B", 4) + c = builder.RegisterCommit("C", 8) + + // Local opening at zero + q2 = builder.LocalOpening("Q01", b) + q3 = builder.LocalOpening("Q02", c) + + // Local opening at but shifted by one + q6 = builder.LocalOpening("Q11", column.Shift(b, 1)) + q7 = builder.LocalOpening("Q12", column.Shift(c, 1)) + + // Local opening but shifted by -1 + q10 = builder.LocalOpening("Q21", column.Shift(b, -1)) + q11 = builder.LocalOpening("Q22", column.Shift(c, -1)) + + } + + comp := wizard.Compile(define, Splitter(4)) + + //after splitting-compilation we expect that the eligible columns and their relevant queries be ignored + assert.Equal(t, column.Committed.String(), comp.Columns.Status("B").String()) + assert.Equal(t, column.Ignored.String(), comp.Columns.Status("C").String()) + + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q2.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q3.ID)) + + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q6.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q7.ID)) + + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q10.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q11.ID)) + + // manually compiles the comp + dummy.Compile(comp) + + proof := wizard.Prove(comp, func(assi *wizard.ProverRuntime) { + // Assigns all the columns + assi.AssignColumn(b.GetColID(), smartvectors.ForTest(2, 3, 4, 5)) + assi.AssignColumn(c.GetColID(), smartvectors.ForTest(6, 7, 8, 9, 10, 11, 12, 13)) + + // And the alleged results + assi.AssignLocalPoint("Q01", field.NewElement(2)) + assi.AssignLocalPoint("Q02", field.NewElement(6)) + + assi.AssignLocalPoint("Q11", field.NewElement(3)) + assi.AssignLocalPoint("Q12", field.NewElement(7)) + + assi.AssignLocalPoint("Q21", field.NewElement(5)) + assi.AssignLocalPoint("Q22", field.NewElement(13)) + + }) + + err := wizard.Verify(comp, proof) + require.NoError(t, err) + +} + +func genAccessors(start, size int) (res []ifaces.Accessor) { + for i := start; i < size+start; i++ { + t := accessors.NewConstant(field.NewElement(uint64(i))) + res = append(res, t) + } + return res +} diff --git a/prover/protocol/compiler/splitter/splitter/stitcher/constraints.go b/prover/protocol/compiler/stitch_split/stitcher/constraints.go similarity index 81% rename from prover/protocol/compiler/splitter/splitter/stitcher/constraints.go rename to prover/protocol/compiler/stitch_split/stitcher/constraints.go index 41e032be3..7d269a2e7 100644 --- a/prover/protocol/compiler/splitter/splitter/stitcher/constraints.go +++ b/prover/protocol/compiler/stitch_split/stitcher/constraints.go @@ -5,7 +5,7 @@ import ( "github.com/consensys/linea-monorepo/prover/protocol/coin" "github.com/consensys/linea-monorepo/prover/protocol/column" "github.com/consensys/linea-monorepo/prover/protocol/column/verifiercol" - alliance "github.com/consensys/linea-monorepo/prover/protocol/compiler/splitter/splitter" + alliance "github.com/consensys/linea-monorepo/prover/protocol/compiler/stitch_split" "github.com/consensys/linea-monorepo/prover/protocol/ifaces" "github.com/consensys/linea-monorepo/prover/protocol/query" "github.com/consensys/linea-monorepo/prover/protocol/variables" @@ -47,6 +47,11 @@ func (ctx stitchingContext) LocalOpening() { if !isColEligible(ctx.Stitchings, q.Pol) { continue } + + switch m := q.Pol.(type) { + case verifiercol.VerifierCol: + utils.Panic("unsupported, received a localOpening over the verifier column %v", m.GetColID()) + } // mark the query as ignored ctx.comp.QueriesParams.MarkAsIgnored(qName) @@ -101,7 +106,7 @@ func (ctx stitchingContext) LocalGlobalConstraints() { ctx.comp.QueriesNoParams.MarkAsIgnored(qName) // adjust the query over the stitching columns - ctx.comp.InsertLocal(round, queryName(qName), ctx.adjustExpression(q.Expression, false)) + ctx.comp.InsertLocal(round, queryName(qName), ctx.adjustExpression(q.Expression, q.DomainSize, false)) case query.GlobalConstraint: board = q.Board() @@ -131,7 +136,7 @@ func (ctx stitchingContext) LocalGlobalConstraints() { // adjust the query over the stitching columns ctx.comp.InsertGlobal(round, queryName(qName), - ctx.adjustExpression(q.Expression, true), + ctx.adjustExpression(q.Expression, q.DomainSize, true), q.NoBoundCancel) default: @@ -143,31 +148,52 @@ func (ctx stitchingContext) LocalGlobalConstraints() { // Takes a sub column and returns the stitching column. // the stitching column is shifted such that the first row agrees with the first row of the sub column. // more detailed, such stitching column agrees with the the sub column up to a subsampling with offset zero. -func getStitchingCol(ctx stitchingContext, col ifaces.Column) ifaces.Column { +// the col should only be either verifiercol or eligible col. +// option is always empty, and used only for the recursive calls over the shifted columns. +func getStitchingCol(ctx stitchingContext, col ifaces.Column, option ...int) ifaces.Column { + var ( + stitchingCol ifaces.Column + newOffset int + round = col.Round() + ) switch m := col.(type) { + // case: verifier columns without shift case verifiercol.VerifierCol: scaling := ctx.MaxSize / col.Size() - return verifiercol.ExpandedVerifCol{ + // expand the veriferCol + stitchingCol = verifiercol.ExpandedVerifCol{ Verifiercol: m, Expansion: scaling, } - } - - // Extract the assumedly single col - natural := column.RootParents(col)[0] + if len(option) != 0 { + // if it is a shifted veriferCol, set the offset for shifting the expanded column + newOffset = option[0] * col.Size() + } + return column.Shift(stitchingCol, newOffset) + case column.Natural: + // find the stitching column + subColInfo := ctx.Stitchings[round].BySubCol[col.GetColID()] + stitchingCol = ctx.comp.Columns.GetHandle(subColInfo.NameBigCol) + scaling := stitchingCol.Size() / col.Size() + if len(option) != 0 { + newOffset = scaling * option[0] + } + newOffset = newOffset + subColInfo.PosInBigCol + return column.Shift(stitchingCol, newOffset) - round := col.Round() - subColInfo := ctx.Stitchings[round].BySubCol[natural.GetColID()] - stitchingCol := ctx.comp.Columns.GetHandle(subColInfo.NameBigCol) + case column.Shifted: + // Shift the stitching column by the right position + offset := column.StackOffsets(col) + col = column.RootParents(col)[0] + res := getStitchingCol(ctx, col, offset) + return res - // Shift the stitching column by the right position - position := column.StackOffsets(col) + default: - scaling := stitchingCol.Size() / natural.Size() - newPosition := scaling*position + subColInfo.PosInBigCol + panic("unsupported") - return column.Shift(stitchingCol, newPosition) + } } func queryName(oldQ ifaces.QueryID) ifaces.QueryID { @@ -179,7 +205,7 @@ func queryName(oldQ ifaces.QueryID) ifaces.QueryID { // This is due to the fact that the verifiercols are not tracked by the compiler and can not be stitched // via [scanAndClassifyEligibleColumns]. func (ctx *stitchingContext) adjustExpression( - expr *symbolic.Expression, + expr *symbolic.Expression, domainSize int, isGlobalConstraint bool, ) ( newExpr *symbolic.Expression, @@ -188,13 +214,11 @@ func (ctx *stitchingContext) adjustExpression( board := expr.Board() metadata := board.ListVariableMetadata() replaceMap := collection.NewMapping[string, *symbolic.Expression]() - domainSize := 0 for i := range metadata { switch m := metadata[i].(type) { case ifaces.Column: // it's always a compiled column - domainSize = m.Size() stitchingCol := getStitchingCol(*ctx, m) replaceMap.InsertNew(m.String(), ifaces.ColumnAsVariable(stitchingCol)) case coin.Info, ifaces.Accessor: diff --git a/prover/protocol/compiler/splitter/splitter/stitcher/stitcher.go b/prover/protocol/compiler/stitch_split/stitcher/stitcher.go similarity index 91% rename from prover/protocol/compiler/splitter/splitter/stitcher/stitcher.go rename to prover/protocol/compiler/stitch_split/stitcher/stitcher.go index 982474f25..9a0344c29 100644 --- a/prover/protocol/compiler/splitter/splitter/stitcher/stitcher.go +++ b/prover/protocol/compiler/stitch_split/stitcher/stitcher.go @@ -4,11 +4,8 @@ import ( "strings" "github.com/consensys/linea-monorepo/prover/maths/common/smartvectors" - "github.com/consensys/linea-monorepo/prover/maths/common/vector" - "github.com/consensys/linea-monorepo/prover/maths/field" "github.com/consensys/linea-monorepo/prover/protocol/column" - "github.com/consensys/linea-monorepo/prover/protocol/column/verifiercol" - alliance "github.com/consensys/linea-monorepo/prover/protocol/compiler/splitter/splitter" + alliance "github.com/consensys/linea-monorepo/prover/protocol/compiler/stitch_split" "github.com/consensys/linea-monorepo/prover/protocol/ifaces" "github.com/consensys/linea-monorepo/prover/protocol/wizard" "github.com/consensys/linea-monorepo/prover/utils" @@ -39,7 +36,7 @@ func Stitcher(minSize, maxSize int) func(comp *wizard.CompiledIOP) { // it assigns the stitching columns and delete the assignment of the sub columns. comp.SubProvers.AppendToInner(comp.NumRounds()-1, func(run *wizard.ProverRuntime) { - for round := range comp.NumRounds() { + for round := range ctx.Stitchings { for subCol := range ctx.Stitchings[round].BySubCol { run.Columns.TryDel(subCol) } @@ -135,8 +132,12 @@ func (ctx *stitchingContext) ScanStitchCommit() { ctx.comp.SubProvers.AppendToInner(round, func(run *wizard.ProverRuntime) { stopTimer := profiling.LogTimer("stitching compiler") defer stopTimer() + var maxSizeGroup int + for idBigCol, subColumns := range ctx.Stitchings[round].ByBigCol { + maxSizeGroup = ctx.MaxSize / subColumns[0].Size() + // Sanity-check sizeBigCol := ctx.comp.Columns.GetHandle(idBigCol).Size() if sizeBigCol != ctx.MaxSize { @@ -154,10 +155,10 @@ func (ctx *stitchingContext) ScanStitchCommit() { witnesses[i] = subColumns[i].GetColAssignment(run) } assignement := smartvectors. - AllocateRegular(len(subColumns) * witnesses[0].Len()).(*smartvectors.Regular) + AllocateRegular(maxSizeGroup * witnesses[0].Len()).(*smartvectors.Regular) for i := range subColumns { for j := 0; j < witnesses[0].Len(); j++ { - (*assignement)[i+j*len(subColumns)] = witnesses[i].Get(j) + (*assignement)[i+j*maxSizeGroup] = witnesses[i].Get(j) } } run.AssignColumn(idBigCol, assignement) @@ -225,13 +226,6 @@ func groupCols(cols []ifaces.Column, numToStitch int) (groups [][]ifaces.Column) groups[i/numToStitch] = append(groups[i/numToStitch], col) } - lastGroup := &groups[len(groups)-1] - zeroCol := verifiercol.NewConstantCol(field.Zero(), size) - - for i := len(*lastGroup); i < numToStitch; i++ { - *lastGroup = append(*lastGroup, zeroCol) - } - return groups } @@ -253,15 +247,25 @@ func (ctx *stitchingContext) stitchGroup(s alliance.Alliance) { // Declare the new columns switch status { case column.Precomputed: - values := make([][]field.Element, len(group)) - for j := range values { - values[j] = smartvectors.IntoRegVec(ctx.comp.Precomputed.MustGet(group[j].GetColID())) + maxSizeGroup := ctx.MaxSize / group[0].Size() + actualSize := len(group) + + // get the assignment of the subColumns and interleave them + witnesses := make([]smartvectors.SmartVector, actualSize) + for i := range witnesses { + witnesses[i] = ctx.comp.Precomputed.MustGet(group[i].GetColID()) + } + assignement := smartvectors. + AllocateRegular(maxSizeGroup * witnesses[0].Len()).(*smartvectors.Regular) + for i := range witnesses { + for j := 0; j < witnesses[0].Len(); j++ { + (*assignement)[i+j*maxSizeGroup] = witnesses[i].Get(j) + } } - assignement := vector.Interleave(values...) + stitchingCol = ctx.comp.InsertPrecomputed( groupedName(group), - smartvectors.NewRegular(assignement), - ) + assignement) case column.Committed: stitchingCol = ctx.comp.InsertCommit( s.Round, diff --git a/prover/protocol/compiler/stitch_split/stitcher/stitcher_test.go b/prover/protocol/compiler/stitch_split/stitcher/stitcher_test.go new file mode 100644 index 000000000..fcb262abe --- /dev/null +++ b/prover/protocol/compiler/stitch_split/stitcher/stitcher_test.go @@ -0,0 +1,322 @@ +package stitcher_test + +import ( + "testing" + + "github.com/consensys/linea-monorepo/prover/maths/common/smartvectors" + "github.com/consensys/linea-monorepo/prover/maths/common/vector" + "github.com/consensys/linea-monorepo/prover/maths/field" + "github.com/consensys/linea-monorepo/prover/protocol/accessors" + "github.com/consensys/linea-monorepo/prover/protocol/column" + "github.com/consensys/linea-monorepo/prover/protocol/column/verifiercol" + "github.com/consensys/linea-monorepo/prover/protocol/compiler/dummy" + "github.com/consensys/linea-monorepo/prover/protocol/compiler/stitch_split/stitcher" + "github.com/consensys/linea-monorepo/prover/protocol/ifaces" + "github.com/consensys/linea-monorepo/prover/protocol/query" + "github.com/consensys/linea-monorepo/prover/protocol/variables" + "github.com/consensys/linea-monorepo/prover/protocol/wizard" + "github.com/consensys/linea-monorepo/prover/symbolic" + sym "github.com/consensys/linea-monorepo/prover/symbolic" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + P1, P2 ifaces.ColID = "P1", "P2" + GLOBAL1, GLOBAL2 ifaces.QueryID = "GLOBAL1", "GLOBAL2" + LOCAL1 ifaces.QueryID = "LOCAL1" +) + +func TestLocalOpening(t *testing.T) { + testStitcher(t, 8, 16, localOpening(4)) +} + +func TestStitcherFibo(t *testing.T) { + testStitcher(t, 16, 32, singlePolyFibo(4)) + testStitcher(t, 4, 8, singlePolyFibo(8)) + testStitcher(t, 8, 16, singlePolyFibo(8)) +} + +func TestStitcherGlobalWithPeriodicSample(t *testing.T) { + testStitcher(t, 16, 64, globalWithPeriodicSample(16, 8, 0)) + testStitcher(t, 64, 256, globalWithPeriodicSample(256, 8, 1)) + testStitcher(t, 64, 128, globalWithPeriodicSample(256, 8, 7)) +} + +func TestStitcherLocalWithPeriodicSample(t *testing.T) { + testStitcher(t, 32, 64, localWithPeriodicSample(256, 8, 0)) + testStitcher(t, 16, 128, localWithPeriodicSample(256, 8, 1)) + testStitcher(t, 64, 256, localWithPeriodicSample(256, 8, 7)) +} + +func TestSplitterGlobalWithVerifColAndPerriodic(t *testing.T) { + testStitcher(t, 8, 64, globalWithVerifColAndPeriodic(8, 4, 0)) + testStitcher(t, 64, 128, globalWithVerifColAndPeriodic(256, 8, 1)) + testStitcher(t, 8, 16, globalWithVerifColAndPeriodic(256, 8, 7)) +} + +func TestLocalEvalWithStatus(t *testing.T) { + + var a, b, c, d ifaces.Column + var q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12 query.LocalOpening + + define := func(builder *wizard.Builder) { + // declare columns of different sizes + a = builder.RegisterCommit("A", 2) + b = builder.RegisterCommit("B", 4) + c = builder.RegisterCommit("C", 8) + d = builder.RegisterCommit("D", 16) + + // Local opening at zero + q1 = builder.LocalOpening("Q00", a) + q2 = builder.LocalOpening("Q01", b) + q3 = builder.LocalOpening("Q02", c) + q4 = builder.LocalOpening("Q03", d) + + // Local opening at but shifted by one + q5 = builder.LocalOpening("Q10", column.Shift(a, 1)) + q6 = builder.LocalOpening("Q11", column.Shift(b, 1)) + q7 = builder.LocalOpening("Q12", column.Shift(c, 1)) + q8 = builder.LocalOpening("Q13", column.Shift(d, 1)) + + // Local opening at but shifted by one + q9 = builder.LocalOpening("Q20", column.Shift(a, -1)) + q10 = builder.LocalOpening("Q21", column.Shift(b, -1)) + q11 = builder.LocalOpening("Q22", column.Shift(c, -1)) + q12 = builder.LocalOpening("Q23", column.Shift(d, -1)) + } + + comp := wizard.Compile(define, stitcher.Stitcher(4, 8)) + + //after stitcing-compilation we expect that the eligible columns and their relevant queries be ignored + assert.Equal(t, column.Proof.String(), comp.Columns.Status("A").String()) + assert.Equal(t, column.Ignored.String(), comp.Columns.Status("B").String()) + assert.Equal(t, column.Committed.String(), comp.Columns.Status("C").String()) + assert.Equal(t, column.Committed.String(), comp.Columns.Status("D").String()) + + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q1.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q2.ID)) + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q3.ID)) + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q4.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q5.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q6.ID)) + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q7.ID)) + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q8.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q9.ID)) + assert.Equal(t, true, comp.QueriesParams.IsIgnored(q10.ID)) + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q11.ID)) + assert.Equal(t, false, comp.QueriesParams.IsIgnored(q12.ID)) + + // manually compiles the comp + dummy.Compile(comp) + + proof := wizard.Prove(comp, func(assi *wizard.ProverRuntime) { + // Assigns all the columns + assi.AssignColumn(a.GetColID(), smartvectors.ForTest(0, 1)) + assi.AssignColumn(b.GetColID(), smartvectors.ForTest(2, 3, 4, 5)) + assi.AssignColumn(c.GetColID(), smartvectors.ForTest(6, 7, 8, 9, 10, 11, 12, 13)) + assi.AssignColumn(d.GetColID(), smartvectors.ForTest(15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30)) + + // And the alleged results + assi.AssignLocalPoint("Q00", field.NewElement(0)) + assi.AssignLocalPoint("Q01", field.NewElement(2)) + assi.AssignLocalPoint("Q02", field.NewElement(6)) + assi.AssignLocalPoint("Q03", field.NewElement(15)) + assi.AssignLocalPoint("Q10", field.NewElement(1)) + assi.AssignLocalPoint("Q11", field.NewElement(3)) + assi.AssignLocalPoint("Q12", field.NewElement(7)) + assi.AssignLocalPoint("Q13", field.NewElement(16)) + assi.AssignLocalPoint("Q20", field.NewElement(1)) + assi.AssignLocalPoint("Q21", field.NewElement(5)) + assi.AssignLocalPoint("Q22", field.NewElement(13)) + assi.AssignLocalPoint("Q23", field.NewElement(30)) + }) + + err := wizard.Verify(comp, proof) + require.NoError(t, err) + +} + +func testStitcher(t *testing.T, minSize, maxSize int, gen func() (wizard.DefineFunc, wizard.ProverStep)) { + + // Activates the logs for easy debugging + logrus.SetLevel(logrus.TraceLevel) + + builder, prover := gen() + comp := wizard.Compile(builder, stitcher.Stitcher(minSize, maxSize), dummy.Compile) + proof := wizard.Prove(comp, prover) + err := wizard.Verify(comp, proof) + + require.NoError(t, err) + + for _, qName := range comp.QueriesNoParams.AllKeysAt(0) { + + switch q := comp.QueriesNoParams.Data(qName).(type) { + + case query.GlobalConstraint: + board := q.Expression.Board() + metadatas := board.ListVariableMetadata() + metadataNames := []string{} + for i := range metadatas { + metadataNames = append(metadataNames, metadatas[i].String()) + } + t.Logf("query %v - with metadata %v", q.ID, metadataNames) + case query.LocalConstraint: + board := q.Expression.Board() + metadatas := board.ListVariableMetadata() + metadataNames := []string{} + for i := range metadatas { + metadataNames = append(metadataNames, metadatas[i].String()) + } + t.Logf("query %v - with metadata %v", q.ID, metadataNames) + } + } +} + +func localOpening(n int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + definer := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, n) + _ = build.LocalOpening("O1", P1) + _ = build.LocalOpening("O2", column.Shift(P1, 3)) + _ = build.LocalOpening("O3", column.Shift(P1, 4)) + _ = build.LocalOpening("O4", column.Shift(P1, -1)) + } + + prover := func(run *wizard.ProverRuntime) { + p1_ := make([]field.Element, n) + for i := range p1_ { + p1_[i].SetUint64(uint64(i)) + } + p1 := smartvectors.NewRegular(p1_) + run.AssignColumn(P1, p1) + run.AssignLocalPoint("O1", p1.Get(0%n)) + run.AssignLocalPoint("O2", p1.Get(3%n)) + run.AssignLocalPoint("O3", p1.Get(4%n)) + run.AssignLocalPoint("O4", p1.Get(n-1)) + } + + return definer, prover + } +} + +func singlePolyFibo(size int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + builder := func(build *wizard.Builder) { + // Number of rows + P1 := build.RegisterCommit(P1, size) // overshadows P + P2 := build.RegisterCommit(P2, size) + + // P(X) = P(X/w) + P(X/w^2) + expr1 := sym.Sub( + sym.Add(column.Shift(P1, 1), P1), + column.Shift(P1, 2)) + + expr2 := sym.Sub( + sym.Add(column.Shift(P2, 1), P2), + column.Shift(P2, 2)) + + _ = build.GlobalConstraint(GLOBAL1, expr1) + _ = build.GlobalConstraint(GLOBAL2, expr2) + // _ = build.LocalConstraint(LOCAL1, sym.Sub(P1, 1)) + } + + prover := func(run *wizard.ProverRuntime) { + x := make([]field.Element, size) + x[0].SetOne() + x[1].SetOne() + for i := 2; i < size; i++ { + x[i].Add(&x[i-1], &x[i-2]) + } + run.AssignColumn(P1, smartvectors.NewRegular(x)) + run.AssignColumn(P2, smartvectors.NewRegular(x)) + } + + return builder, prover + } +} + +func globalWithPeriodicSample(size, period, offset int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + + builder := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, size) // overshadows P + _ = build.GlobalConstraint(GLOBAL1, variables.NewPeriodicSample(period, offset).Mul(ifaces.ColumnAsVariable(P1))) + } + + prover := func(run *wizard.ProverRuntime) { + v := vector.Repeat(field.One(), size) + for i := 0; i < size; i++ { + if i%period == offset { + v[i].SetZero() + } + } + run.AssignColumn(P1, smartvectors.NewRegular(v)) + } + + return builder, prover + } +} + +func localWithPeriodicSample(size, period, offset int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + + builder := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, size) // overshadows P + _ = build.LocalConstraint(GLOBAL1, variables.NewPeriodicSample(period, offset).Mul(ifaces.ColumnAsVariable(P1))) + } + + prover := func(run *wizard.ProverRuntime) { + v := vector.Repeat(field.One(), size) + for i := 0; i < size; i++ { + if i%period == offset { + v[i].SetZero() + } + } + run.AssignColumn(P1, smartvectors.NewRegular(v)) + } + + return builder, prover + } +} + +func globalWithVerifColAndPeriodic(size, period, offset int) func() (wizard.DefineFunc, wizard.ProverStep) { + return func() (wizard.DefineFunc, wizard.ProverStep) { + + builder := func(build *wizard.Builder) { + P1 := build.RegisterCommit(P1, size) + verifcol1 := verifiercol.NewFromAccessors(genAccessors(0, size), field.Zero(), size) + verifcol2 := verifiercol.NewFromAccessors(genAccessors(2, size), field.Zero(), size) + _ = build.GlobalConstraint(LOCAL1, + symbolic.Sub( + + symbolic.Mul(symbolic.Sub(1, P1), + verifcol2), + + symbolic.Mul(variables.NewPeriodicSample(period, offset), + symbolic.Add(2, verifcol1))), + ) + } + + prover := func(run *wizard.ProverRuntime) { + v := vector.Repeat(field.One(), size) + for i := 0; i < size; i++ { + if i%period == offset { + v[i].SetZero() + } + } + run.AssignColumn(P1, smartvectors.NewRegular(v)) + } + + return builder, prover + } +} + +func genAccessors(start, size int) (res []ifaces.Accessor) { + for i := start; i < size+start; i++ { + t := accessors.NewConstant(field.NewElement(uint64(i))) + res = append(res, t) + } + return res +} diff --git a/prover/protocol/query/global.go b/prover/protocol/query/global.go index e3f399dd1..63dbbc4c4 100644 --- a/prover/protocol/query/global.go +++ b/prover/protocol/query/global.go @@ -162,6 +162,9 @@ func (cs GlobalConstraint) Check(run ifaces.Runtime) error { stop -= offsetRange.Max } + start = max(start, 0) + stop = min(stop, cs.DomainSize) + for i := start; i < stop; i++ { resx := res.Get(i) diff --git a/prover/protocol/query/global_test.go b/prover/protocol/query/global_test.go index d4734ad7d..3a69cf216 100644 --- a/prover/protocol/query/global_test.go +++ b/prover/protocol/query/global_test.go @@ -16,7 +16,7 @@ import ( func TestGlobal(t *testing.T) { runTest(t, pythagoreTriplet, true) runTest(t, fibonacci, true) - + runTest(t, testDummyShifted, true) } func runTest(t *testing.T, gen GlobalConstraintGenerator, expectedCorrect bool) { @@ -98,3 +98,25 @@ func pythagoreTriplet() (wizard.DefineFunc, wizard.ProverStep) { return define, hLProver } + +func testDummyShifted() (wizard.DefineFunc, wizard.ProverStep) { + var ( + X, Y ifaces.ColID = "X", "Y" + ) + define := func(build *wizard.Builder) { + A := build.RegisterCommit(X, 4) + B := build.RegisterCommit(Y, 4) + + expr := symbolic.Sub(column.Shift(A, 1), + symbolic.Mul(2, column.Shift(B, 1))) + + build.InsertGlobal(0, "Q", expr) + } + Prover := func(run *wizard.ProverRuntime) { + x := smartvectors.ForTest(2, 8, 4, 0) + y := smartvectors.ForTest(1, 4, 2, 0) + run.AssignColumn(X, x) + run.AssignColumn(Y, y) + } + return define, Prover +} diff --git a/prover/zkevm/prover/hash/packing/cld.go b/prover/zkevm/prover/hash/packing/cld.go index c8ee6bde3..d6ef3ecd4 100644 --- a/prover/zkevm/prover/hash/packing/cld.go +++ b/prover/zkevm/prover/hash/packing/cld.go @@ -101,6 +101,7 @@ func newDecomposition(comp *wizard.CompiledIOP, inp decompositionInputs) decompo Table: decomposed.decomposedLimbs, TableLen: decomposed.decomposedLen, MaxLen: inp.param.LaneSizeBytes(), + Name: inp.Name, } decomposed.pa = dedicated.LengthConsistency(comp, lcInputs) @@ -113,8 +114,8 @@ func (decomposed *decomposition) insertCommit(comp *wizard.CompiledIOP) { createCol := common.CreateColFn(comp, DECOMPOSITION+"_"+decomposed.Inputs.Name, decomposed.size) for x := 0; x < decomposed.nbSlices; x++ { decomposed.decomposedLimbs = append(decomposed.decomposedLimbs, createCol("Decomposed_Limbs", x)) - decomposed.decomposedLen = append(decomposed.decomposedLen, createCol("Decomposed_Len", x)) - decomposed.decomposedLenPowers = append(decomposed.decomposedLenPowers, createCol("Decomposed_Len_Powers", x)) + decomposed.decomposedLen = append(decomposed.decomposedLen, createCol("Decomposed_Len_%v", x)) + decomposed.decomposedLenPowers = append(decomposed.decomposedLenPowers, createCol("Decomposed_Len_Powers_%v", x)) } decomposed.paIsZero = make([]wizard.ProverAction, decomposed.nbSlices) diff --git a/prover/zkevm/prover/hash/packing/dedicated/len_consistency.go b/prover/zkevm/prover/hash/packing/dedicated/len_consistency.go index c0153a3e9..bb24b77aa 100644 --- a/prover/zkevm/prover/hash/packing/dedicated/len_consistency.go +++ b/prover/zkevm/prover/hash/packing/dedicated/len_consistency.go @@ -2,7 +2,6 @@ package dedicated import ( "slices" - "strconv" "github.com/consensys/linea-monorepo/prover/maths/common/smartvectors" "github.com/consensys/linea-monorepo/prover/maths/field" @@ -23,6 +22,8 @@ type LcInputs struct { TableLen []ifaces.Column // max length in bytes. MaxLen int + // name of the table + Name string } // lengthConsistency stores the intermediate columns for [LengthConsistency] function. @@ -44,7 +45,7 @@ type lengthConsistency struct { func LengthConsistency(comp *wizard.CompiledIOP, inp LcInputs) *lengthConsistency { var ( - name = strconv.Itoa(len(comp.ListCommitments())) + name = inp.Name numCol = len(inp.Table) size = inp.Table[0].Size() numBytes = inp.MaxLen @@ -60,7 +61,7 @@ func LengthConsistency(comp *wizard.CompiledIOP, inp LcInputs) *lengthConsistenc for j := 0; j < numCol; j++ { res.bytesLen[j] = make([]ifaces.Column, numBytes) for k := range res.bytesLen[0] { - res.bytesLen[j][k] = createCol("BytesLen", j, k) + res.bytesLen[j][k] = createCol("BYTE_LEN_%v_%v", j, k) } }