arnaucube
arnaucube/circomPublic Self-hosted repository
optimize optimize constraints
Jordi Baylina jordi@baylina.cat
Sat, 18 Jul 2020 14:55:00 +0200
commit

9fe8be9828800c73562b391810b18630751bc795

parent

7e24d6f57d58a7d1aef34e36af14ef8af3ec9dd4

5 files changed, 265 insertions(+), 34 deletions(-)

jump to
M package-lock.json → package-lock.json

@@ -143,6 +143,11 @@ "version": "1.6.48",

"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" }, + "blakejs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", + "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",

@@ -571,9 +576,9 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",

"dev": true }, "fastfile": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.2.tgz", - "integrity": "sha512-rS9vpAM4DS+xacKSH0MVCqr/Y5SiIAiunJTYSuq1H9nfRpkP37ewgnjH3FztziKBUQB2xPYS/WxEEjHl+pA5Fg==" + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.7.tgz", + "integrity": "sha512-Zk7sdqsV6DsN/rhjULDfCCowPiMDsziTMFicdkrKN80yybr/6YFf9H91ELXN85dVEf6EYkVR5EHkZNc0dMqZKA==" }, "ffiasm": { "version": "0.0.2",

@@ -586,11 +591,13 @@ "yargs": "^15.3.1"

} }, "ffjavascript": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.1.2.tgz", - "integrity": "sha512-zB1dhgBjJlvyk2VQIQzFSpUJmanYQYDR/Fo1KhZnaNW5chMFJT55qz0dx1LMKrAklBbidKzz2/C7dgibxQF94g==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.4.tgz", + "integrity": "sha512-XFeWcjUDFPavN+DDOxhE8p5MOhZQJc9oO1Sj4ml1pyjqNhS1ujEamcjFyK0cctdnat61i7lvpTYzdtS3RYDC8w==", "requires": { - "big-integer": "^1.6.48" + "big-integer": "^1.6.48", + "wasmcurves": "0.0.4", + "worker-threads": "^1.0.0" } }, "ffwasm": {

@@ -1154,27 +1161,12 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",

"dev": true }, "r1csfile": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.5.tgz", - "integrity": "sha512-B+BdKPb/WUTp4N/3X4d1Spgx9Ojx5tFVejGZRJxpTtzq34mC8Vi/czWfiPj85V8kud31lCfYcZ16z7+czvM0Sw==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.14.tgz", + "integrity": "sha512-7m4eWpnbjkwGGUaRmIAJc4w+HvaeBPJXUKHIqLkHeD9Yyjem6/EHmlgDVl+4hWNWGZqdhXuMqWSH9+O6QOXBdw==", "requires": { - "fastfile": "0.0.1", - "ffjavascript": "0.1.0" - }, - "dependencies": { - "fastfile": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.1.tgz", - "integrity": "sha512-Fk8PWafGWGEUw7oPq/dJen92ASxknCEy4ZC8n4VEvSwCp/jcReyEmVoWsRIWTf+IvAp2MzvFi54vOPeK2LQZtQ==" - }, - "ffjavascript": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.1.0.tgz", - "integrity": "sha512-dmKlUasSfvUcxBm8nCSKl2x7EFJsXA7OVP8XLFA03T2+6mAc3IiVLC2ambEVOcMOhyhl0vJfVZjM9f9d38D1rw==", - "requires": { - "big-integer": "^1.6.48" - } - } + "fastfile": "0.0.7", + "ffjavascript": "0.2.4" } }, "regexpp": {

@@ -1472,6 +1464,15 @@ "requires": {

"big-integer": "^1.6.48" } }, + "wasmcurves": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.0.4.tgz", + "integrity": "sha512-c/Tob+F/7jJhep1b2qtj54r4nkGaRifNbQ1OJx8cBBFH1RlHbWIbISHWONClOxiVwy/JZOpbN4SgvSX/4lF80A==", + "requires": { + "big-integer": "^1.6.42", + "blakejs": "^1.1.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",

@@ -1491,6 +1492,11 @@ "version": "1.2.3",

"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true + }, + "worker-threads": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/worker-threads/-/worker-threads-1.0.0.tgz", + "integrity": "sha512-vK6Hhvph8oLxocEJIlc3YfGAZhm210uGzjZsXSu+JYLAQ/s/w4Tqgl60JrdH58hW8NSGP4m3bp8a92qPXgX05w==" }, "wrap-ansi": { "version": "6.2.0",
M package.json → package.json

@@ -31,12 +31,12 @@ },

"dependencies": { "chai": "^4.2.0", "circom_runtime": "0.0.6", - "fastfile": "0.0.2", + "fastfile": "0.0.7", "ffiasm": "0.0.2", - "ffjavascript": "0.1.2", + "ffjavascript": "0.2.4", "ffwasm": "0.0.7", "fnv-plus": "^1.3.1", - "r1csfile": "0.0.5", + "r1csfile": "0.0.14", "tmp-promise": "^2.0.2", "wasmbuilder": "0.0.10" },
M src/bigarray.js → src/bigarray.js

@@ -1,4 +1,4 @@

-const SUBARRAY_SIZE = 0x10000; +const SUBARRAY_SIZE = 0x40000; const BigArrayHandler = { get: function(obj, prop) {

@@ -19,7 +19,7 @@

class _BigArray { constructor (initSize) { this.length = initSize || 0; - this.arr = []; + this.arr = new Array(SUBARRAY_SIZE); for (let i=0; i<initSize; i+=SUBARRAY_SIZE) { this.arr[i/SUBARRAY_SIZE] = new Array(Math.min(SUBARRAY_SIZE, initSize - i));

@@ -39,7 +39,7 @@ setElement(idx, value) {

idx = parseInt(idx); const idx1 = Math.floor(idx / SUBARRAY_SIZE); if (!this.arr[idx1]) { - this.arr[idx1] = []; + this.arr[idx1] = new Array(SUBARRAY_SIZE); } const idx2 = idx % SUBARRAY_SIZE; this.arr[idx1][idx2] = value;
M src/compiler.js → src/compiler.js

@@ -65,12 +65,16 @@ if (options.reduceConstraints) {

if (ctx.verbose) console.log("Reduce Constraints"); // Repeat while reductions are performed +/* let oldNConstrains = -1; while (ctx.constraints.length != oldNConstrains) { if (ctx.verbose) console.log("Reducing constraints: "+ctx.constraints.length); oldNConstrains = ctx.constraints.length; reduceConstrains(ctx); } +*/ + + reduceConstrains(ctx); } if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length);

@@ -252,6 +256,227 @@ ctx.constraints = newConstraints;

} function reduceConstrains(ctx) { + const sig2constraint = {}; + let removedSignals = {}; + let nRemoved; + let lIdx; + + + let possibleConstraints = new Array(ctx.constraints.length); + let nextPossibleConstraints; + for (let i=0; i<ctx.constraints.length; i++) { + const insertedSig = {}; + const c = ctx.constraints[i]; + for (let s in c.a.coefs) { + if (!insertedSig[s]) { + if (!sig2constraint[s]) sig2constraint[s] = []; + sig2constraint[s].push(i); + insertedSig[s] = true; + } + } + for (let s in c.b.coefs) { + if (!insertedSig[s]) { + if (!sig2constraint[s]) sig2constraint[s] = []; + sig2constraint[s].push(i); + insertedSig[s] = true; + } + } + for (let s in c.c.coefs) { + if (!insertedSig[s]) { + if (!sig2constraint[s]) sig2constraint[s] = []; + sig2constraint[s].push(i); + insertedSig[s] = true; + } + } + possibleConstraints[i] = i; + } + + while (possibleConstraints.length >0) { + nextPossibleConstraints = {}; + removedSignals = {}; + nRemoved = 0; + lIdx = {}; + for (let i=0;i<possibleConstraints.length;i++) { + if ((ctx.verbose)&&(i%10000 == 0)) { + console.log(`reducing constraints: ${i}/${possibleConstraints.length} reduced: ${nRemoved}`); + } + const c = ctx.constraints[possibleConstraints[i]]; + if (!c) continue; + + // Swap a and b if b has more variables. + if (Object.keys(c.b).length > Object.keys(c.a).length) { + const aux = c.a; + c.a=c.b; + c.b=aux; + } + + // Mov to C if possible. + if (isConstant(c.a)) { + const ct = {t: "N", v: c.a.coefs[sONE]}; + c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c); + c.a = { t: "LC", coefs: {} }; + c.b = { t: "LC", coefs: {} }; + } + if (isConstant(c.b)) { + const ct = {t: "N", v: c.b.coefs[sONE]}; + c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c); + c.a = { t: "LC", coefs: {} }; + c.b = { t: "LC", coefs: {} }; + } + + if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) { + const freeC = substituteRemoved(c.c); + const isolatedSignal = getFirstInternalSignal(ctx, freeC); + if (isolatedSignal) { + // console.log(isolatedSignal); + // console.log(freeC); + removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal); + if (lIdx[isolatedSignal]) { + lIdx[isolatedSignal].forEach( (s) => { + removedSignals[s] = substitute(removedSignals[s], isolatedSignal, removedSignals[isolatedSignal]); + }); + } + + addTolIdx(removedSignals[isolatedSignal], isolatedSignal); + ctx.constraints[possibleConstraints[i]] = null; + nRemoved ++; + + sig2constraint[isolatedSignal].forEach( (s) => { + nextPossibleConstraints[s] = true; + }); + } + } + } + + nextPossibleConstraints = Object.keys(nextPossibleConstraints); + + for (let i=0; i<nextPossibleConstraints.length;i++) { + const c = ctx.constraints[nextPossibleConstraints[i]]; + if (c) { + const nc = { + a: substituteRemoved(c.a), + b: substituteRemoved(c.b), + c: substituteRemoved(c.c) + }; + if (ctx.lc.isZero(nc)) { + delete ctx.constraints[nextPossibleConstraints[i]]; + } else { + ctx.constraints[nextPossibleConstraints[i]] = nc; + } + } + } + + for (let s in removedSignals) { + + let lSignal = ctx.signals[s]; + while (lSignal.e>=0) { + lSignal = ctx.signals[lSignal.e]; + } + + lSignal.c = ctx.stDISCARDED; + } + + possibleConstraints = nextPossibleConstraints; + + } + + let o=0; + for (let i=0; i<ctx.constraints.length;i++) { + if (ctx.constraints[i]) { + if (!ctx.lc.isZero(ctx.constraints[i])) { + ctx.constraints[o] = ctx.constraints[i]; + o++; + } + } + } + + ctx.constraints.length = o; + + function getFirstInternalSignal(ctx, l) { + for (let k in l.coefs) { + k = Number(k); + const signal = ctx.signals[k]; + if ((signal.c == ctx.stINTERNAL)&&(!ctx.F.isZero(l.coefs[k])) &&(!removedSignals[k])) return k; + } + return null; + } + + function isolateSignal(lc, s) { + const eq = { + t: "LC", + coefs: {} + }; + const invCoef = ctx.F.inv(lc.coefs[s]); + for (const k in lc.coefs) { + if (k != s) { + const v = ctx.F.mul( ctx.F.neg(lc.coefs[k]), invCoef); + if (!ctx.F.isZero(v)) { + eq.coefs[k] = v; + } + } + } + return eq; + } + + function substituteRemoved(lc) { + const newLc = ctx.lc._clone(lc); + for (let k in lc.coefs) { + if (removedSignals[k]) { + delete newLc.coefs[k]; + for (let k2 in removedSignals[k].coefs) { + const newP = ctx.F.mul(removedSignals[k].coefs[k2], lc.coefs[k]); + if (!ctx.F.isZero(newP)) { + if (newLc.coefs[k2]) { + newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP); + if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2]; + } else { + newLc.coefs[k2] = newP; + } + } + } + } + } + return newLc; + } + + function substitute(lc, s, eq) { + if (!lc.coefs[s]) return lc; + const newLc = ctx.lc._clone(lc); + delete newLc.coefs[s]; + for (let k2 in eq.coefs) { + const newP = ctx.F.mul(eq.coefs[k2], lc.coefs[s]); + if (!ctx.F.isZero(newP)) { + if (newLc.coefs[k2]) { + newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP); + if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2]; + } else { + newLc.coefs[k2] = newP; + } + } + } + return newLc; + } + + function isConstant(l) { + for (let k in l.coefs) { + if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false; + } + if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false; + return true; + } + + function addTolIdx(lc, newS) { + for (let s in lc.coefs) { + if (!lIdx[s]) lIdx[s] = []; + lIdx[s].push(newS); + } + } + + +} + + +function reduceConstrains_old(ctx) { indexVariables(); let possibleConstraints = ctx.constraints; let ii=0;
M src/r1csfile.js → src/r1csfile.js

@@ -79,7 +79,7 @@ }

} for (let i=0; i<arr.length; i++) { await fd.writeULE64(arr[i]); - if ((ctx.verbose)&&(i%100000)) console.log("writing wire2label map: ", i); + if ((ctx.verbose)&&(i%100000 == 0)) console.log(`writing wire2label map: ${i}/${arr.length}`); } const mapSize = fd.pos - pMapSize - 8;