Skip to content

Commit

Permalink
feat: added api.Tag and api.AddCounter to measure number of constrain…
Browse files Browse the repository at this point in the history
…ts in portion of circuit
  • Loading branch information
gbotrel committed Nov 16, 2021
1 parent e116a82 commit 7d23961
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 8 deletions.
6 changes: 2 additions & 4 deletions debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import (
"strings"
)

var light = true

func Stack() string {
var sbb strings.Builder
WriteStack(&sbb)
return sbb.String()
}

func WriteStack(sbb *strings.Builder) {
func WriteStack(sbb *strings.Builder, forceClean ...bool) {
// derived from: https://golang.org/pkg/runtime/#example_Frames
// we stop when func name == Define as it is where the gnark circuit code should start

Expand All @@ -37,7 +35,7 @@ func WriteStack(sbb *strings.Builder) {
function := fe[len(fe)-1]
file := frame.File

if light {
if !Debug || (len(forceClean) > 1 && forceClean[0]) {
if strings.Contains(function, "runtime.gopanic") {
continue
}
Expand Down
4 changes: 1 addition & 3 deletions debug/debug_full.go → debug/debug_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,4 @@

package debug

func init() {
light = false
}
const Debug = true
6 changes: 6 additions & 0 deletions debug/debug_unset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//go:build !debug
// +build !debug

package debug

const Debug = false
7 changes: 7 additions & 0 deletions frontend/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,11 @@ type API interface {
// from the backend point of view, it's equivalent to a user-supplied witness
// except, the solver is going to assign it a value, not the caller
NewHint(f hint.Function, inputs ...interface{}) Variable

// Tag creates a tag at a given place in a circuit. The state of the tag may contain informations needed to
// measure constraints, variables and coefficients creations through AddCounter
Tag(name string) Tag

// AddCounter measures the number of constraints, variables and coefficients created between two tags
AddCounter(from, to Tag)
}
22 changes: 22 additions & 0 deletions frontend/counter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package frontend

import (
"fmt"
)

// Tag contains informations needed to measure and display statistics of a delimited piece of circuit
type Tag struct {
Name string
vID, cID int
}

// Counter contains measurements of useful statistics between two Tag
type Counter struct {
From, To Tag
NbVariables int
NbConstraints int
}

func (c Counter) String() string {
return fmt.Sprintf("%s to %s: %d variables, %d constraints", c.From.Name, c.To.Name, c.NbVariables, c.NbConstraints)
}
3 changes: 3 additions & 0 deletions frontend/cs.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type constraintSystem struct {

mDebug map[int]int // maps constraint ID to debugInfo id

counters []Counter // statistic counters

curveID ecc.ID
}

Expand Down Expand Up @@ -118,6 +120,7 @@ func newConstraintSystem(curveID ecc.ID, initialCapacity ...int) constraintSyste
mDebug: make(map[int]int),
mHints: make(map[int]compiled.Hint),
mHintsConstrained: make(map[int]bool),
counters: make([]Counter, 0),
}

cs.coeffs[compiled.CoeffIdZero].SetInt64(0)
Expand Down
22 changes: 22 additions & 0 deletions frontend/cs_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,25 @@ func (cs *constraintSystem) addDebugInfo(errName string, i ...interface{}) int {
cs.debugInfo = append(cs.debugInfo, l)
return len(cs.debugInfo) - 1
}

// Tag creates a tag at a given place in a circuit. The state of the tag may contain informations needed to
// measure constraints, variables and coefficients creations through AddCounter
func (cs *constraintSystem) Tag(name string) Tag {
_, file, line, _ := runtime.Caller(1)

return Tag{
Name: fmt.Sprintf("%s[%s:%d]", name, filepath.Base(file), line),
vID: len(cs.internal.variables),
cID: len(cs.constraints),
}
}

// AddCounter measures the number of constraints, variables and coefficients created between two tags
func (cs *constraintSystem) AddCounter(from, to Tag) {
cs.counters = append(cs.counters, Counter{
From: from,
To: to,
NbVariables: to.vID - from.vID,
NbConstraints: to.cID - from.cID,
})
}
20 changes: 20 additions & 0 deletions frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,22 @@ func Compile(curveID ecc.ID, zkpID backend.ID, circuit Circuit, opts ...func(opt
case backend.GROTH16:
ccs, err = cs.toR1CS(curveID)
case backend.PLONK:
// TODO update cs.counters
ccs, err = cs.toSparseR1CS(curveID)
default:
panic("not implemented")
}

// print counters
// TODO we need a way to access these through APIs, printing is not great.
if opt.displayCounters && len(cs.counters) > 0 {
fmt.Printf("counters [%s - %s]:\n", zkpID, curveID)
for _, c := range cs.counters {
fmt.Printf("\t%s\n", c)
}
fmt.Println()
}

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -153,6 +165,7 @@ func Value(value interface{}) Variable {
type CompileOption struct {
capacity int
ignoreUnconstrainedInputs bool
displayCounters bool
}

// WithOutput is a Compile option that specifies the estimated capacity needed for internal variables and constraints
Expand All @@ -168,3 +181,10 @@ func IgnoreUnconstrainedInputs(opt *CompileOption) error {
opt.ignoreUnconstrainedInputs = true
return nil
}

// DisplayCounters when set, the Compile function will display counters added through api.AddCounter
// after the post-compile phase ran
func DisplayCounters(opt *CompileOption) error {
opt.displayCounters = true
return nil
}
12 changes: 11 additions & 1 deletion test/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/backend/plonk"
"github.com/consensys/gnark/backend/witness"
"github.com/consensys/gnark/debug"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/internal/utils"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -364,7 +365,16 @@ func (assert *Assert) compile(circuit frontend.Circuit, curveID ecc.ID, backendI
return nil, err
}

_ccs, err := frontend.Compile(curveID, backendID, circuit, compileOpts...)
// if debug flag is set, add frontend.DisplayCounters
var cOpts []func(opt *frontend.CompileOption) error
if debug.Debug {
cOpts = make([]func(opt *frontend.CompileOption) error, len(compileOpts)+1)
copy(cOpts, compileOpts)
cOpts[len(cOpts)-1] = frontend.DisplayCounters // it's ok if we have a duplicate
} else {
cOpts = compileOpts
}
_ccs, err := frontend.Compile(curveID, backendID, circuit, cOpts...)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrCompilationNotDeterministic, err)
}
Expand Down
9 changes: 9 additions & 0 deletions test/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,15 @@ func (e *engine) NewHint(f hint.Function, inputs ...interface{}) frontend.Variab
return frontend.Value(result)
}

func (e *engine) Tag(name string) frontend.Tag {
// do nothing, we don't measure constraints with the test engine
return frontend.Tag{Name: name}
}

func (e *engine) AddCounter(from, to frontend.Tag) {
// do nothing, we don't measure constraints with the test engine
}

func (e *engine) toBigInt(i1 interface{}) big.Int {
if v1, ok := i1.(frontend.Variable); ok {
return v1.GetWitnessValue(e.curveID)
Expand Down

0 comments on commit 7d23961

Please sign in to comment.