Skip to content

Commit

Permalink
merge dev
Browse files Browse the repository at this point in the history
  • Loading branch information
siq1 committed Dec 10, 2024
2 parents 29e2af9 + 3201cdd commit 4f1b7c6
Show file tree
Hide file tree
Showing 14 changed files with 864 additions and 36 deletions.
105 changes: 104 additions & 1 deletion ecgo/builder/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ func (builder *builder) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) f

// returns res = Σ(vars) or res = vars[0] - Σ(vars[1:]) if sub == true.
func (builder *builder) add(vars []int, sub bool) frontend.Variable {
// check if all variables are constants
allConst := true
if sum, ok := builder.constantValue(vars[0]); ok {
for _, x := range vars[1:] {
if v, ok := builder.constantValue(x); ok {
if sub {
sum = builder.field.Sub(sum, v)
} else {
sum = builder.field.Add(sum, v)
}
} else {
allConst = false
break
}
}
if allConst {
return builder.toVariable(sum)
}
}

coef := make([]constraint.Element, len(vars))
coef[0] = builder.tOne
if sub {
Expand All @@ -75,6 +95,9 @@ func (builder *builder) add(vars []int, sub bool) frontend.Variable {
// Neg returns the negation of the given variable.
func (builder *builder) Neg(i frontend.Variable) frontend.Variable {
v := builder.toVariableId(i)
if c, ok := builder.constantValue(v); ok {
return builder.toVariable(builder.field.Neg(c))
}
coef := []constraint.Element{builder.field.Neg(builder.tOne)}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.LinComb,
Expand All @@ -87,6 +110,20 @@ func (builder *builder) Neg(i frontend.Variable) frontend.Variable {
// Mul computes the product of the given variables.
func (builder *builder) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable {
vars := builder.toVariableIds(append([]frontend.Variable{i1, i2}, in...)...)
allConst := true
if sum, ok := builder.constantValue(vars[0]); ok {
for _, x := range vars[1:] {
if v, ok := builder.constantValue(x); ok {
sum = builder.field.Mul(sum, v)
} else {
allConst = false
break
}
}
if allConst {
return builder.toVariable(sum)
}
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.Mul,
Inputs: vars,
Expand All @@ -99,6 +136,18 @@ func (builder *builder) DivUnchecked(i1, i2 frontend.Variable) frontend.Variable
vars := builder.toVariableIds(i1, i2)
v1 := vars[0]
v2 := vars[1]
c1, ok1 := builder.constantValue(v1)
c2, ok2 := builder.constantValue(v2)
if ok1 && ok2 {
if c2.IsZero() {
if c1.IsZero() {
return builder.toVariable(constraint.Element{})
}
panic("division by zero")
}
inv, _ := builder.field.Inverse(c2)
return builder.toVariable(builder.field.Mul(c1, inv))
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.Div,
X: v1,
Expand All @@ -113,6 +162,15 @@ func (builder *builder) Div(i1, i2 frontend.Variable) frontend.Variable {
vars := builder.toVariableIds(i1, i2)
v1 := vars[0]
v2 := vars[1]
c1, ok1 := builder.constantValue(v1)
c2, ok2 := builder.constantValue(v2)
if ok1 && ok2 {
if c2.IsZero() {
panic("division by zero")
}
inv, _ := builder.field.Inverse(c2)
return builder.toVariable(builder.field.Mul(c1, inv))
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.Div,
X: v1,
Expand Down Expand Up @@ -160,6 +218,17 @@ func (builder *builder) Xor(_a, _b frontend.Variable) frontend.Variable {
vars := builder.toVariableIds(_a, _b)
a := vars[0]
b := vars[1]
c1, ok1 := builder.constantValue(a)
c2, ok2 := builder.constantValue(b)
if ok1 && ok2 {
builder.AssertIsBoolean(_a)
builder.AssertIsBoolean(_b)
t := builder.field.Sub(c1, c2)
if t.IsZero() {
return builder.toVariable(constraint.Element{})
}
return builder.toVariable(builder.tOne)
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.BoolBinOp,
X: a,
Expand All @@ -174,6 +243,16 @@ func (builder *builder) Or(_a, _b frontend.Variable) frontend.Variable {
vars := builder.toVariableIds(_a, _b)
a := vars[0]
b := vars[1]
c1, ok1 := builder.constantValue(a)
c2, ok2 := builder.constantValue(b)
if ok1 && ok2 {
builder.AssertIsBoolean(_a)
builder.AssertIsBoolean(_b)
if c1.IsZero() && c2.IsZero() {
return builder.toVariable(constraint.Element{})
}
return builder.toVariable(builder.tOne)
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.BoolBinOp,
X: a,
Expand All @@ -188,6 +267,16 @@ func (builder *builder) And(_a, _b frontend.Variable) frontend.Variable {
vars := builder.toVariableIds(_a, _b)
a := vars[0]
b := vars[1]
c1, ok1 := builder.constantValue(a)
c2, ok2 := builder.constantValue(b)
if ok1 && ok2 {
builder.AssertIsBoolean(_a)
builder.AssertIsBoolean(_b)
if c1.IsZero() || c2.IsZero() {
return builder.toVariable(constraint.Element{})
}
return builder.toVariable(builder.tOne)
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.BoolBinOp,
X: a,
Expand All @@ -207,7 +296,15 @@ func (builder *builder) Select(i0, i1, i2 frontend.Variable) frontend.Variable {
// ensures that cond is boolean
builder.AssertIsBoolean(cond)

v := builder.Sub(i1, i2) // no constraint is recorded
cst, ok := builder.constantValue(builder.toVariableId(cond))
if ok {
if cst.IsZero() {
return i2
}
return i1
}

v := builder.Sub(i1, i2)
w := builder.Mul(cond, v)
return builder.Add(w, i2)
}
Expand Down Expand Up @@ -246,6 +343,12 @@ func (builder *builder) Lookup2(b0, b1 frontend.Variable, i0, i1, i2, i3 fronten
// IsZero returns 1 if the given variable is zero, otherwise returns 0.
func (builder *builder) IsZero(i1 frontend.Variable) frontend.Variable {
a := builder.toVariableId(i1)
if c, ok := builder.constantValue(a); ok {
if c.IsZero() {
return builder.toVariable(builder.tOne)
}
return builder.toVariable(constraint.Element{})
}
builder.instructions = append(builder.instructions, irsource.Instruction{
Type: irsource.IsZero,
X: a,
Expand Down
20 changes: 20 additions & 0 deletions ecgo/builder/api_assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ import (
// AssertIsEqual adds an assertion that i1 is equal to i2.
func (builder *builder) AssertIsEqual(i1, i2 frontend.Variable) {
x := builder.toVariableId(builder.Sub(i1, i2))
v, xConstant := builder.constantValue(x)
if xConstant {
if !v.IsZero() {
panic("AssertIsEqual will never be satisfied on nonzero constant")
}
return
}
builder.constraints = append(builder.constraints, irsource.Constraint{
Typ: irsource.Zero,
Var: x,
Expand All @@ -22,6 +29,13 @@ func (builder *builder) AssertIsEqual(i1, i2 frontend.Variable) {
// AssertIsDifferent constrains i1 and i2 to have different values.
func (builder *builder) AssertIsDifferent(i1, i2 frontend.Variable) {
x := builder.toVariableId(builder.Sub(i1, i2))
v, xConstant := builder.constantValue(x)
if xConstant {
if v.IsZero() {
panic("AssertIsDifferent will never be satisfied on zero constant")
}
return
}
builder.constraints = append(builder.constraints, irsource.Constraint{
Typ: irsource.NonZero,
Var: x,
Expand All @@ -31,6 +45,12 @@ func (builder *builder) AssertIsDifferent(i1, i2 frontend.Variable) {
// AssertIsBoolean adds an assertion that the variable is either 0 or 1.
func (builder *builder) AssertIsBoolean(i1 frontend.Variable) {
x := builder.toVariableId(i1)
if b, ok := builder.constantValue(x); ok {
if !(b.IsZero() || builder.field.IsOne(b)) {
panic("assertIsBoolean failed: constant is not 0 or 1")
}
return
}
builder.constraints = append(builder.constraints, irsource.Constraint{
Typ: irsource.Bool,
Var: x,
Expand Down
31 changes: 27 additions & 4 deletions ecgo/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type builder struct {

nbExternalInput int
maxVar int
varConstId []int
constValues []constraint.Element

// defers (for gnark API)
defers []func(frontend.API) error
Expand All @@ -58,6 +60,8 @@ func (r *Root) newBuilder(nbExternalInput int) *builder {
builder.tOne = builder.field.One()

builder.maxVar = nbExternalInput
builder.varConstId = make([]int, nbExternalInput+1)
builder.constValues = make([]constraint.Element, 1)

return &builder
}
Expand Down Expand Up @@ -106,11 +110,24 @@ func (builder *builder) Compile() (constraint.ConstraintSystem, error) {

// ConstantValue returns always returns (nil, false) now, since the Golang frontend doesn't know the values of variables.
func (builder *builder) ConstantValue(v frontend.Variable) (*big.Int, bool) {
return nil, false
coeff, ok := builder.constantValue(builder.toVariableId(v))
if !ok {
return nil, false
}
return builder.field.ToBigInt(coeff), true
}

func (builder *builder) constantValue(x int) (constraint.Element, bool) {
i := builder.varConstId[x]
if i == 0 {
return constraint.Element{}, false
}
return builder.constValues[i], true
}

func (builder *builder) addVarId() int {
builder.maxVar += 1
builder.varConstId = append(builder.varConstId, 0)
return builder.maxVar
}

Expand All @@ -124,7 +141,10 @@ func (builder *builder) ceToId(x constraint.Element) int {
ExtraId: 0,
Const: x,
})
return builder.addVarId()
res := builder.addVarId()
builder.constValues = append(builder.constValues, x)
builder.varConstId[res] = len(builder.constValues) - 1
return res
}

// toVariable will return (and allocate if neccesary) an Expression from given value
Expand All @@ -147,6 +167,10 @@ func (builder *builder) toVariableId(input interface{}) int {
}
}

func (builder *builder) toVariable(input interface{}) frontend.Variable {
return newVariable(builder.toVariableId(input))
}

// toVariables return frontend.Variable corresponding to inputs and the total size of the linear expressions
func (builder *builder) toVariableIds(in ...frontend.Variable) []int {
r := make([]int, 0, len(in))
Expand Down Expand Up @@ -195,8 +219,7 @@ func (builder *builder) newHintForId(id solver.HintID, nbOutputs int, inputs []f

res := make([]frontend.Variable, nbOutputs)
for i := 0; i < nbOutputs; i++ {
builder.maxVar += 1
res[i] = newVariable(builder.maxVar)
res[i] = builder.addVar()
}
return res, nil
}
Expand Down
6 changes: 6 additions & 0 deletions ecgo/utils/gnarkexpr/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ func init() {
}
}

// gnark uses uint32
const MaxVariables = (1 << 31) - 100

func NewVar(x int) Expr {
if x < 0 || x >= MaxVariables {
panic("variable id out of range")
}
v := builder.InternalVariable(uint32(x))
t := reflect.ValueOf(v).Index(0).Interface().(Expr)
if t.WireID() != x {
Expand Down
1 change: 1 addition & 0 deletions expander_compiler/src/circuit/ir/dest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use super::{
pub mod tests;

pub mod display;
pub mod mul_fanout_limit;

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Instruction<C: Config> {
Expand Down
Loading

0 comments on commit 4f1b7c6

Please sign in to comment.