diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index 90943f3f0e..0aa15de555 100644 --- a/internal/stats/latest_stats.csv +++ b/internal/stats/latest_stats.csv @@ -83,10 +83,10 @@ math/emulated/secp256k1_64,bn254,groth16,1037,1890 math/emulated/secp256k1_64,bls12_377,groth16,1037,1890 math/emulated/secp256k1_64,bls12_381,groth16,1037,1890 math/emulated/secp256k1_64,bw6_761,groth16,1037,1890 -math/emulated/secp256k1_64,bn254,plonk,4280,4178 -math/emulated/secp256k1_64,bls12_377,plonk,4280,4178 -math/emulated/secp256k1_64,bls12_381,plonk,4280,4178 -math/emulated/secp256k1_64,bw6_761,plonk,4280,4178 +math/emulated/secp256k1_64,bn254,plonk,4025,3923 +math/emulated/secp256k1_64,bls12_377,plonk,4025,3923 +math/emulated/secp256k1_64,bls12_381,plonk,4025,3923 +math/emulated/secp256k1_64,bw6_761,plonk,4025,3923 pairing_bls12377,bn254,groth16,0,0 pairing_bls12377,bls12_377,groth16,0,0 pairing_bls12377,bls12_381,groth16,0,0 @@ -99,7 +99,7 @@ pairing_bls12381,bn254,groth16,946127,1565085 pairing_bls12381,bls12_377,groth16,0,0 pairing_bls12381,bls12_381,groth16,0,0 pairing_bls12381,bw6_761,groth16,0,0 -pairing_bls12381,bn254,plonk,3249854,3113604 +pairing_bls12381,bn254,plonk,3184319,3048069 pairing_bls12381,bls12_377,plonk,0,0 pairing_bls12381,bls12_381,plonk,0,0 pairing_bls12381,bw6_761,plonk,0,0 @@ -107,7 +107,7 @@ pairing_bn254,bn254,groth16,607339,995018 pairing_bn254,bls12_377,groth16,0,0 pairing_bn254,bls12_381,groth16,0,0 pairing_bn254,bw6_761,groth16,0,0 -pairing_bn254,bn254,plonk,2053232,1971118 +pairing_bn254,bn254,plonk,1987697,1905583 pairing_bn254,bls12_377,plonk,0,0 pairing_bn254,bls12_381,plonk,0,0 pairing_bn254,bw6_761,plonk,0,0 @@ -115,7 +115,7 @@ pairing_bw6761,bn254,groth16,1782130,2981326 pairing_bw6761,bls12_377,groth16,0,0 pairing_bw6761,bls12_381,groth16,0,0 pairing_bw6761,bw6_761,groth16,0,0 -pairing_bw6761,bn254,plonk,6088164,5845211 +pairing_bw6761,bn254,plonk,6022629,5779676 pairing_bw6761,bls12_377,plonk,0,0 pairing_bw6761,bls12_381,plonk,0,0 pairing_bw6761,bw6_761,plonk,0,0 @@ -123,7 +123,7 @@ scalar_mul_G1_bn254,bn254,groth16,55279,87825 scalar_mul_G1_bn254,bls12_377,groth16,0,0 scalar_mul_G1_bn254,bls12_381,groth16,0,0 scalar_mul_G1_bn254,bw6_761,groth16,0,0 -scalar_mul_G1_bn254,bn254,plonk,201599,194727 +scalar_mul_G1_bn254,bn254,plonk,199552,192680 scalar_mul_G1_bn254,bls12_377,plonk,0,0 scalar_mul_G1_bn254,bls12_381,plonk,0,0 scalar_mul_G1_bn254,bw6_761,plonk,0,0 @@ -131,7 +131,7 @@ scalar_mul_P256,bn254,groth16,75326,121582 scalar_mul_P256,bls12_377,groth16,0,0 scalar_mul_P256,bls12_381,groth16,0,0 scalar_mul_P256,bw6_761,groth16,0,0 -scalar_mul_P256,bn254,plonk,270364,261074 +scalar_mul_P256,bn254,plonk,262173,252883 scalar_mul_P256,bls12_377,plonk,0,0 scalar_mul_P256,bls12_381,plonk,0,0 scalar_mul_P256,bw6_761,plonk,0,0 @@ -139,7 +139,7 @@ scalar_mul_secp256k1,bn254,groth16,55961,88905 scalar_mul_secp256k1,bls12_377,groth16,0,0 scalar_mul_secp256k1,bls12_381,groth16,0,0 scalar_mul_secp256k1,bw6_761,groth16,0,0 -scalar_mul_secp256k1,bn254,plonk,204108,197145 +scalar_mul_secp256k1,bn254,plonk,202061,195098 scalar_mul_secp256k1,bls12_377,plonk,0,0 scalar_mul_secp256k1,bls12_381,plonk,0,0 scalar_mul_secp256k1,bw6_761,plonk,0,0 diff --git a/std/internal/logderivarg/logderivarg.go b/std/internal/logderivarg/logderivarg.go index 2d3e5e041e..35a354b28a 100644 --- a/std/internal/logderivarg/logderivarg.go +++ b/std/internal/logderivarg/logderivarg.go @@ -57,7 +57,7 @@ func init() { // GetHints returns all hints used in this package func GetHints() []solver.Hint { - return []solver.Hint{countHint} + return []solver.Hint{countHint, batchDivBySubHint} } // Table is a vector of vectors. @@ -122,9 +122,50 @@ func Build(api frontend.API, table Table, queries Table) error { multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { rowCoeffs, challenge := randLinearCoefficients(api, nbRow, commitment) var lp frontend.Variable = 0 - for i := range table { - tmp := api.DivUnchecked(exps[i], api.Sub(challenge, randLinearCombination(api, rowCoeffs, table[i]))) - lp = api.Add(lp, tmp) + + // For constant single-column tables with PlonkAPI, merge Sub+DivUnchecked + // into a single PLONK gate per table entry (2 gates instead of 3). + plonkAPI, useOptimizedTable := api.(frontend.PlonkAPI) + if useOptimizedTable && constTable && nbRow == 1 { + // verify all constant values fit in int (required by AddPlonkConstraint) + for i := range table { + cv, _ := api.Compiler().ConstantValue(table[i][0]) + if cv == nil || !cv.IsInt64() { + useOptimizedTable = false + break + } + } + } else { + useOptimizedTable = false + } + + if useOptimizedTable { + n := len(table) + hintInputs := make([]frontend.Variable, 1+2*n) + hintInputs[0] = challenge + for i := range table { + hintInputs[1+i] = exps[i] + hintInputs[1+n+i] = table[i][0] + } + quotients, err := api.NewHint(batchDivBySubHint, n, hintInputs...) + if err != nil { + return fmt.Errorf("batch div hint: %w", err) + } + for i := range table { + constVal, _ := api.Compiler().ConstantValue(table[i][0]) + c := int(constVal.Int64()) + // Verify quotient[i] * (challenge - c) == exps[i] in one PLONK gate: + // qM*q*ch + qL*q + qR*ch + qO*exps + qC = 0 + // 1*q*ch + (-c)*q + 0*ch + (-1)*exps + 0 = 0 + // => q*(ch - c) = exps + plonkAPI.AddPlonkConstraint(quotients[i], challenge, exps[i], -c, 0, -1, 1, 0) + lp = api.Add(lp, quotients[i]) + } + } else { + for i := range table { + tmp := api.DivUnchecked(exps[i], api.Sub(challenge, randLinearCombination(api, rowCoeffs, table[i]))) + lp = api.Add(lp, tmp) + } } var rp frontend.Variable = 0 @@ -245,6 +286,32 @@ func randLinearCombinationExt(extapi fieldextension.Field, rowCoeffs []fieldexte return res } +// batchDivBySubHint computes outputs[i] = inputs[1+i] / (inputs[0] - inputs[1+n+i]) +// where n = len(outputs). +// inputs: [challenge, numerator_0, ..., numerator_{n-1}, denomOffset_0, ..., denomOffset_{n-1}] +// outputs: [quotient_0, ..., quotient_{n-1}] +func batchDivBySubHint(m *big.Int, inputs []*big.Int, outputs []*big.Int) error { + n := len(outputs) + if len(inputs) != 1+2*n { + return fmt.Errorf("expected %d inputs, got %d", 1+2*n, len(inputs)) + } + challenge := inputs[0] + diff := new(big.Int) + for i := 0; i < n; i++ { + numerator := inputs[1+i] + tableVal := inputs[1+n+i] + diff.Sub(challenge, tableVal) + diff.Mod(diff, m) + diffInv := new(big.Int).ModInverse(diff, m) + if diffInv == nil { + return fmt.Errorf("no modular inverse at index %d", i) + } + outputs[i].Mul(numerator, diffInv) + outputs[i].Mod(outputs[i], m) + } + return nil +} + func countHint(m *big.Int, inputs []*big.Int, outputs []*big.Int) error { if len(inputs) <= 2 { return fmt.Errorf("at least two input required")