Skip to content

Commit 85d2dbc

Browse files
hawkadrianivokubCopilot
authored
feat: add example for printing constraints (#1643)
Co-authored-by: Ivo Kubjas <[email protected]> Co-authored-by: Ivo Kubjas <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 9873a56 commit 85d2dbc

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed

examples/print_constraints/doc.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Package print_constraints demonstrates how to print constraints from a compiled constraint system.
2+
//
3+
// This example shows how to:
4+
// 1. Compile a circuit using [frontend.Compile]
5+
// 2. Type assert the compiled constraint system to access constraint-specific methods
6+
// 3. Retrieve and print constraints in a human-readable format
7+
//
8+
// This is useful for debugging circuits and understanding the constraint structure,
9+
// similar to the visual view available on https://play.gnark.io/
10+
package print_constraints
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package print_constraints
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/consensys/gnark-crypto/ecc"
8+
"github.com/consensys/gnark/constraint"
9+
"github.com/consensys/gnark/frontend"
10+
"github.com/consensys/gnark/frontend/cs/r1cs"
11+
)
12+
13+
// CubicCircuit defines a simple cubic equation circuit
14+
// x**3 + x + 5 == y
15+
type CubicCircuit struct {
16+
X frontend.Variable `gnark:"x"`
17+
Y frontend.Variable `gnark:",public"`
18+
}
19+
20+
// Define declares the circuit constraints
21+
//
22+
// x**3 + x + 5 == y
23+
func (circuit *CubicCircuit) Define(api frontend.API) error {
24+
x3 := api.Mul(circuit.X, circuit.X, circuit.X)
25+
api.AssertIsEqual(circuit.Y, api.Add(x3, circuit.X, 5))
26+
return nil
27+
}
28+
29+
// Example_printR1CS demonstrates how to print constraints from an R1CS constraint system.
30+
//
31+
// This example shows how to:
32+
// 1. Compile a circuit using frontend.Compile with [r1cs.NewBuilder]
33+
// 2. Assert that the compiled constraint system has GetR1Cs() method
34+
// 3. Retrieve constraints using GetR1Cs()
35+
// 4. Print each constraint using String() method with the constraint system as [constraint.Resolver]
36+
func Example_printR1CS() {
37+
// Compile the circuit
38+
ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &CubicCircuit{})
39+
if err != nil {
40+
fmt.Fprintf(os.Stderr, "failed to compile circuit: %v\n", err)
41+
return
42+
}
43+
44+
// Assert that the constraint system has the `GetR1Cs()` method. This allows us
45+
// to get the R1CS constraints, but without needing to assert to a specific
46+
// curve implementation.
47+
48+
// Type assert to get access to R1CS-specific methods
49+
r1csSystem, ok := ccs.(interface{ GetR1Cs() []constraint.R1C })
50+
if !ok {
51+
fmt.Fprintf(os.Stderr, "constraint system is not an R1CS\n")
52+
return
53+
}
54+
55+
// Get all constraints
56+
constraints := r1csSystem.GetR1Cs()
57+
58+
// Print constraint system statistics
59+
fmt.Printf("Constraint System Type: R1CS\n")
60+
fmt.Printf("Number of constraints: %d\n", len(constraints))
61+
fmt.Printf("Number of public variables: %d\n", ccs.GetNbPublicVariables())
62+
fmt.Printf("Number of secret variables: %d\n", ccs.GetNbSecretVariables())
63+
fmt.Printf("Number of internal variables: %d\n", ccs.GetNbInternalVariables())
64+
fmt.Println()
65+
66+
// Print each constraint
67+
fmt.Println("Constraints:")
68+
fmt.Println("-----------")
69+
for i, r1c := range constraints {
70+
// The String() method requires a Resolver (the constraint system implements this)
71+
// This formats the constraint as "L ⋅ R == O"
72+
fmt.Printf("Constraint %d: %s\n", i, r1c.String(ccs))
73+
}
74+
75+
// Output:
76+
// Constraint System Type: R1CS
77+
// Number of constraints: 3
78+
// Number of public variables: 2
79+
// Number of secret variables: 1
80+
// Number of internal variables: 2
81+
//
82+
// Constraints:
83+
// -----------
84+
// Constraint 0: x ⋅ x == v0
85+
// Constraint 1: v0 ⋅ x == v1
86+
// Constraint 2: 1 ⋅ Y == 5 + x + v1
87+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package print_constraints
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/consensys/gnark-crypto/ecc"
8+
"github.com/consensys/gnark/constraint"
9+
"github.com/consensys/gnark/frontend"
10+
"github.com/consensys/gnark/frontend/cs/scs"
11+
)
12+
13+
// Example_printSparseR1CS demonstrates how to print constraints from a SparseR1CS constraint system.
14+
//
15+
// This example shows how to:
16+
// 1. Compile a circuit using frontend.Compile with [scs.NewBuilder] (PLONK/SparseR1CS)
17+
// 2. Assert that the compiled constraint system has GetSparseR1Cs() method
18+
// 3. Retrieve constraints using GetSparseR1Cs()
19+
// 4. Print each constraint using String() method with the constraint system as [constraint.Resolver]
20+
func Example_printSparseR1CS() {
21+
// Compile the circuit using SparseR1CS (PLONK) builder
22+
ccs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &CubicCircuit{})
23+
if err != nil {
24+
fmt.Fprintf(os.Stderr, "failed to compile circuit: %v\n", err)
25+
return
26+
}
27+
28+
// Type assert to get access to SparseR1CS-specific methods
29+
scsSystem, ok := ccs.(interface{ GetSparseR1Cs() []constraint.SparseR1C })
30+
if !ok {
31+
fmt.Fprintf(os.Stderr, "constraint system is not a SparseR1CS\n")
32+
return
33+
}
34+
35+
// Get all constraints
36+
constraints := scsSystem.GetSparseR1Cs()
37+
38+
// Print constraint system statistics
39+
fmt.Printf("Constraint System Type: SparseR1CS (PLONK)\n")
40+
fmt.Printf("Number of constraints: %d\n", len(constraints))
41+
fmt.Printf("Number of public variables: %d\n", ccs.GetNbPublicVariables())
42+
fmt.Printf("Number of secret variables: %d\n", ccs.GetNbSecretVariables())
43+
fmt.Printf("Number of internal variables: %d\n", ccs.GetNbInternalVariables())
44+
fmt.Println()
45+
46+
// Print each constraint
47+
fmt.Println("Constraints:")
48+
fmt.Println("-----------")
49+
for i, sparseR1c := range constraints {
50+
// The String() method requires a Resolver (the constraint system implements this)
51+
// This formats the constraint as "qL⋅xa + qR⋅xb + qO⋅xc + qM⋅(xaxb) + qC == 0"
52+
fmt.Printf("Constraint %d: %s\n", i, sparseR1c.String(ccs))
53+
}
54+
55+
// Output:
56+
// Constraint System Type: SparseR1CS (PLONK)
57+
// Number of constraints: 4
58+
// Number of public variables: 1
59+
// Number of secret variables: 1
60+
// Number of internal variables: 3
61+
//
62+
// Constraints:
63+
// -----------
64+
// Constraint 0: 0 + 0 + -1⋅v0 + 1⋅(x×x) + 0 == 0
65+
// Constraint 1: 0 + 0 + -1⋅v1 + 1⋅(v0×x) + 0 == 0
66+
// Constraint 2: x + v1 + -1⋅v2 + 5 == 0
67+
// Constraint 3: Y + -1⋅v2 + 0 + 0 == 0
68+
}

0 commit comments

Comments
 (0)