def run_one_test(nInBits, nCopies): nOutBits = nInBits (in0v, in1v, typv) = randutil.rand_ckt(nOutBits, nInBits) inputs = randutil.rand_inputs(nInBits, nCopies) circuit = CircuitProver(nCopies, 2**nInBits, [in0v], [in1v], [typv]) circuit.set_inputs(inputs) z1 = [Defs.gen_random() for _ in xrange(0, nOutBits)] z2 = [Defs.gen_random() for _ in xrange(0, circuit.nCopyBits)] circuit.set_z(z1, z2, None, None, True) # mlExt of outputs outflat = util.flatten(circuit.ckt_outputs) inLayer_mults = LayerComputeBeta(nOutBits + circuit.nCopyBits, z1 + z2) assert len(outflat) == len(inLayer_mults.outputs) inLayermul = util.mul_vecs(inLayer_mults.outputs, outflat) inLayerExt = sum(inLayermul) % Defs.prime w1 = [Defs.gen_random() for _ in xrange(0, nInBits)] w2 = [Defs.gen_random() for _ in xrange(0, nInBits)] w3 = [Defs.gen_random() for _ in xrange(0, circuit.nCopyBits)] initOutputs = circuit.get_outputs() assert inLayerExt == (initOutputs[0] + sum(initOutputs)) % Defs.prime for i in xrange(0, len(w3)): circuit.next_round(w3[i]) circuit.get_outputs() for i in xrange(0, len(w1)): circuit.next_round(w1[i]) circuit.get_outputs() for i in xrange(0, len(w2)): circuit.next_round(w2[i]) finalOutputs = circuit.get_outputs() # check the outputs by computing mlext of layer input directly inflat = util.flatten(inputs) v1_mults = LayerComputeBeta(circuit.layer_inbits[0] + circuit.nCopyBits, w1 + w3) assert len(inflat) == len(v1_mults.outputs) v1inmul = util.mul_vecs(v1_mults.outputs, inflat) v1 = sum(v1inmul) % Defs.prime v2_mults = LayerComputeBeta(circuit.layer_inbits[0] + circuit.nCopyBits, w2 + w3) assert len(inflat) == len(v2_mults.outputs) v2inmul = util.mul_vecs(v2_mults.outputs, inflat) v2 = sum(v2inmul) % Defs.prime assert v1 == finalOutputs[0] assert v2 == sum(finalOutputs) % Defs.prime
def __init__(self, nInBits, circuit, in0v, in1v, typev, muxv=None): # pylint: disable=protected-access self.nInBits = nInBits self.circuit = circuit self.nOutBits = util.clog2(len(in0v)) self.roundNum = 0 self.muls = None self.compute_v = [] self.inputs = [] self.output = [] if muxv is None: # fake it muxv = [0] * len(in0v) assert len(in0v) == len(in1v) and len(in0v) == len(muxv) and len( in0v) == len(typev) # h computation subckt self.compute_h = LayerComputeH(self, circuit.comp_h) # beta computation subckt self.compute_beta = LayerComputeBeta(self.circuit.nCopyBits, None, circuit.comp_b) # z1chi computation subckt # this uses the Verifier's fast beta eval code self.compute_z1chi = None self.compute_z1chi_2 = None # v circuits are per-input---we collapse inputs in first nCopyBits rounds for _ in xrange(0, 2**self.nInBits): lcv_tmp = LayerComputeV(self.circuit.nCopyBits, circuit.comp_v) # outputs are collapsed lcv_tmp.expand_outputs = lcv_tmp.multiple_passes = False self.compute_v.append(lcv_tmp) # after finishing the first nCopyBits rounds, we only need one ComputeV circuit, # and everything is now 2nd order, so we only need three eval points, not four self.compute_v_final = LayerComputeV(self.nInBits, circuit.comp_v_fin) self.compute_v_final.set_other_factors([util.THIRD_EVAL_POINT]) # pergate computation subckts for "early" rounds self.gates = [] max_muxbit = 0 for (out, (in0, in1, mx, tp)) in enumerate(izip(in0v, in1v, muxv, typev)): assert issubclass(tp, gateprover._GateProver) self.gates.append(tp(True, in0, in1, out, self, mx)) if mx > max_muxbit: max_muxbit = mx muxlen = max_muxbit + 1 assert len(self.circuit.muxbits ) >= muxlen, "Expected %d muxbits, found %d" % ( muxlen, len(self.circuit.muxbits))
def speed_test(num_tests): nBits = random.randint(3, 8) inputs = [ [ Defs.gen_random() for _ in xrange(0, nBits) ] for _ in xrange(0, num_tests) ] lcb = LayerComputeBeta(nBits) lcb.other_factors = [] runtime = time.time() for idx in xrange(0, num_tests): lcb.set_inputs(inputs[idx]) runtime = time.time() - runtime runtime2 = time.time() for idx in xrange(0, num_tests): VerifierIOMLExt.compute_beta(inputs[idx]) runtime2 = time.time() - runtime2 print "nBits: %d\nLayerComputeBeta: %f\nVerifierIOMLExt: %f\n" % (nBits, runtime, runtime2)
def run_one_test(nbits, squawk, nbins, pattern): z = [Defs.gen_random() for _ in xrange(0, nbits)] inv = [Defs.gen_random() for _ in xrange(0, (2**nbits) - nbins)] if pattern is 0: inv += [0 for _ in xrange(0, nbins)] elif pattern is 1: inv += [1 for _ in xrange(0, nbins)] elif pattern == 2: inv += [(i % 2) for i in xrange(0, nbins)] elif pattern == 3: inv += [((i + 1) % 2) for i in xrange(0, nbins)] else: inv += [random.randint(0, 1) for _ in xrange(0, nbins)] assert len(inv) == (2**nbits) Defs.track_fArith = True fa = Defs.fArith() oldrec = fa.new_cat("old") newrec = fa.new_cat("new") nw2rec = fa.new_cat("nw2") oldbeta = LayerComputeBeta(nbits, z, oldrec) oldval = sum(util.mul_vecs(oldbeta.outputs, inv)) % Defs.prime oldrec.did_mul(len(inv)) oldrec.did_add(len(inv) - 1) newcomp = VerifierIOMLExt(z, newrec) newval = newcomp.compute(inv) nw2comp = LayerComputeV(nbits, nw2rec) nw2comp.other_factors = [] nw2comp.set_inputs(inv) for zz in z: nw2comp.next_round(zz) nw2val = nw2comp.prevPassValue assert oldval == newval, "error for inputs (new) %s : %s" % (str(z), str(inv)) assert oldval == nw2val, "error for inputs (nw2) %s : %s" % (str(z), str(inv)) if squawk: print print "nbits: %d" % nbits print "OLD: %s" % str(oldrec) print "NEW: %s" % str(newrec) print "NW2: %s" % str(nw2rec) betacomp = VerifierIOMLExt.compute_beta(z) beta_lo = random.randint(0, 2**nbits - 1) beta_hi = random.randint(beta_lo, 2**nbits - 1) betacomp2 = VerifierIOMLExt.compute_beta(z, None, 1, beta_lo, beta_hi) # make sure that the right range was generated, and correctly assert len(betacomp) == len(betacomp2) assert all([b is None for b in betacomp2[:beta_lo]]) assert all([b is not None for b in betacomp2[beta_lo:beta_hi + 1]]) assert all([b is None for b in betacomp2[beta_hi + 1:]]) assert all([ b2 == b if b2 is not None else True for (b, b2) in zip(betacomp, betacomp2) ]) return newrec.get_counts()
import os.path import random import sys import time except: assert False else: sys.path.insert(1, os.path.abspath(os.path.join(sys.path[0], os.pardir))) from libfennel import util from libfennel.circuitverifier import VerifierIOMLExt from libfennel.defs import Defs from libfennel.layercompute import LayerComputeBeta nOutBits = 4 lcv = LayerComputeBeta(nOutBits) def run_test(): # pylint: disable=global-variable-undefined,redefined-outer-name tinputs = [Defs.gen_random() for _ in xrange(0, nOutBits)] taus = [Defs.gen_random() for _ in xrange(0, nOutBits)] lcv.set_inputs(tinputs) assert lcv.outputs == VerifierIOMLExt.compute_beta(tinputs) inputs = [util.chi(util.numToBin(x, nOutBits), tinputs) for x in xrange(0, 2**nOutBits)] global scratch global outputs scratch = list(inputs) outputs = list(inputs)
class LayerProver(object): def __init__(self, nInBits, circuit, in0v, in1v, typev, muxv=None): # pylint: disable=protected-access self.nInBits = nInBits self.circuit = circuit self.nOutBits = util.clog2(len(in0v)) self.roundNum = 0 self.muls = None self.compute_v = [] self.inputs = [] self.output = [] if muxv is None: # fake it muxv = [0] * len(in0v) assert len(in0v) == len(in1v) and len(in0v) == len(muxv) and len( in0v) == len(typev) # h computation subckt self.compute_h = LayerComputeH(self, circuit.comp_h) # beta computation subckt self.compute_beta = LayerComputeBeta(self.circuit.nCopyBits, None, circuit.comp_b) # z1chi computation subckt # this uses the Verifier's fast beta eval code self.compute_z1chi = None self.compute_z1chi_2 = None # v circuits are per-input---we collapse inputs in first nCopyBits rounds for _ in xrange(0, 2**self.nInBits): lcv_tmp = LayerComputeV(self.circuit.nCopyBits, circuit.comp_v) # outputs are collapsed lcv_tmp.expand_outputs = lcv_tmp.multiple_passes = False self.compute_v.append(lcv_tmp) # after finishing the first nCopyBits rounds, we only need one ComputeV circuit, # and everything is now 2nd order, so we only need three eval points, not four self.compute_v_final = LayerComputeV(self.nInBits, circuit.comp_v_fin) self.compute_v_final.set_other_factors([util.THIRD_EVAL_POINT]) # pergate computation subckts for "early" rounds self.gates = [] max_muxbit = 0 for (out, (in0, in1, mx, tp)) in enumerate(izip(in0v, in1v, muxv, typev)): assert issubclass(tp, gateprover._GateProver) self.gates.append(tp(True, in0, in1, out, self, mx)) if mx > max_muxbit: max_muxbit = mx muxlen = max_muxbit + 1 assert len(self.circuit.muxbits ) >= muxlen, "Expected %d muxbits, found %d" % ( muxlen, len(self.circuit.muxbits)) # set new inputs def set_inputs(self, inputs): assert len( inputs) == self.circuit.nCopies, "Got inputs for the wrong #copies" self.inputs = inputs for inX in xrange(0, 2**self.nInBits): # transpose input matrix inXVals = [inCopy[inX] for inCopy in inputs] self.compute_v[inX].set_inputs(inXVals) # set a new z vector def set_z(self, z1, z2, z1_2, muls, project_line): self.roundNum = 0 self.compute_beta.set_inputs(z2) if z1_2 is not None: self.compute_z1chi = VerifierIOMLExt.compute_beta( z1, self.circuit.comp_chi, muls[0]) self.compute_z1chi_2 = VerifierIOMLExt.compute_beta( z1_2, self.circuit.comp_chi, muls[1]) assert len( muls ) == 2, "Got muls of wrong size with non-None z1_2 in set_z()" else: self.compute_z1chi = VerifierIOMLExt.compute_beta( z1, self.circuit.comp_chi) self.compute_z1chi_2 = None assert muls is None # are we going to project a line at the beginning of the next round? self.compute_h.project_line = project_line # loop over all the gates and make them update their z coeffs for g in self.gates: g.set_z() # compute fj[0], fj[1], fj[-1], and maybe fj[2] def compute_outputs(self): # if we're at the last round, just return h coefficients if self.roundNum == self.circuit.nCopyBits + 2 * self.nInBits: self.output = self.compute_h.output return inEarlyRounds = True if self.roundNum >= self.circuit.nCopyBits: inEarlyRounds = False if inEarlyRounds: # go through each copy of the circuit out = [0, 0, 0, 0] for copy in xrange(0, len(self.compute_beta.outputs), 2): vals = [0, 0, 0, 0] # go through each gate, summing contribution from this copy for g in self.gates: g.compute_outputs(copy) for j in xrange(0, 4): vals[j] += g.output[j] vals[j] %= Defs.prime vals[0] *= self.compute_beta.outputs[copy] vals[0] %= Defs.prime out[0] += vals[0] out[0] %= Defs.prime vals[1] *= self.compute_beta.outputs[copy + 1] vals[1] %= Defs.prime out[1] += vals[1] out[1] %= Defs.prime # index with [copy >> 1] because expand_outputs is false in compute_beta vals[2] *= self.compute_beta.outputs_fact[0][copy >> 1] vals[2] %= Defs.prime out[2] += vals[2] out[2] %= Defs.prime vals[3] *= self.compute_beta.outputs_fact[1][copy >> 1] vals[3] %= Defs.prime out[3] += vals[3] out[3] %= Defs.prime if self.circuit.comp_out: self.circuit.comp_out.did_add(4 * len(self.gates) + 4) self.circuit.comp_out.did_mul(4) self.output = util.interpolate_cubic(out, self.circuit.comp_out) else: # late rounds: only one set of gates over which to sum; # in these rounds we are updating w1 and then w2 out = [0, 0, 0] for g in self.gates: g.compute_outputs() # sum contributions from this gate for j in xrange(0, 3): out[j] += g.output[j] out[j] %= Defs.prime for j in xrange(0, 3): out[j] *= self.compute_beta.prevPassValue if self.circuit.comp_out: self.circuit.comp_out.did_add(3 * len(self.gates)) self.circuit.comp_out.did_mul(3) self.output = util.interpolate_quadratic(out, self.circuit.comp_out) # do updates upon receiving new random value def next_round(self, val): assert self.roundNum < self.circuit.nCopyBits + 2 * self.nInBits inLateRounds = True # do beta and V updates if self.roundNum < self.circuit.nCopyBits: inLateRounds = False self.compute_beta.next_round(val) for cv in self.compute_v: cv.next_round(val) # no gate updates in early rounds: gate circuits don't update state # gotta do some juggling now that we're done with the w3 updates if self.roundNum == self.circuit.nCopyBits - 1: # set up compute_v_final with prevPassValues from the per-input compute_v instances inputs = [cv.prevPassValue for cv in self.compute_v] assert all(elm is not None for elm in inputs) self.compute_v_final.set_inputs(inputs) # prepare gates for final rounds for g in self.gates: g.set_early(False) g.set_z() # updating w1 or w2, which requires updating compute_v_final and the gates if inLateRounds: self.compute_v_final.next_round(val) for g in self.gates: g.next_round(val) # finally, update the h_vals (needs to be done after compute_v and compute_beta are updated) self.compute_h.next_round(val) self.roundNum += 1
def run_one_test(nInBits, nCopies): nOutBits = nInBits circuit = _DummyCircuitProver(nCopies) (in0v, in1v, typv) = randutil.rand_ckt(nOutBits, nInBits) typc = [tc.cgate for tc in typv] inputs = randutil.rand_inputs(nInBits, nCopies) # compute outputs ckt = ArithCircuit() inCktLayer = ArithCircuitInputLayer(ckt, nOutBits) outCktLayer = ArithCircuitLayer(ckt, inCktLayer, in0v, in1v, typc) ckt.layers = [inCktLayer, outCktLayer] outputs = [] for inp in inputs: ckt.run(inp) outputs.append(ckt.outputs) z1 = [Defs.gen_random() for _ in xrange(0, nOutBits)] z2 = [Defs.gen_random() for _ in xrange(0, circuit.nCopyBits)] outLayer = LayerProver(nInBits, circuit, in0v, in1v, typv) outLayer.set_inputs(inputs) outLayer.set_z(z1, z2, None, None, True) # mlExt of outputs outflat = util.flatten(outputs) inLayer_mults = LayerComputeBeta(nOutBits + outLayer.circuit.nCopyBits, z1 + z2) assert len(outflat) == len(inLayer_mults.outputs) inLayermul = util.mul_vecs(inLayer_mults.outputs, outflat) inLayerExt = sum(inLayermul) % Defs.prime w3 = [Defs.gen_random() for _ in xrange(0, circuit.nCopyBits)] w1 = [Defs.gen_random() for _ in xrange(0, nInBits)] w2 = [Defs.gen_random() for _ in xrange(0, nInBits)] outLayer.compute_outputs() initOutputs = outLayer.output assert inLayerExt == (initOutputs[0] + sum(initOutputs)) % Defs.prime for i in xrange(0, len(w3)): outLayer.next_round(w3[i]) outLayer.compute_outputs() for i in xrange(0, len(w1)): outLayer.next_round(w1[i]) outLayer.compute_outputs() for i in xrange(0, len(w2)): outLayer.next_round(w2[i]) outLayer.compute_outputs() finalOutputs = outLayer.output # check the outputs by computing mlext of layer input directly inflat = util.flatten(inputs) v1_mults = LayerComputeBeta(outLayer.nInBits + outLayer.circuit.nCopyBits, w1 + w3) assert len(inflat) == len(v1_mults.outputs) v1inmul = util.mul_vecs(v1_mults.outputs, inflat) v1 = sum(v1inmul) % Defs.prime v2_mults = LayerComputeBeta(outLayer.nInBits + outLayer.circuit.nCopyBits, w2 + w3) assert len(inflat) == len(v2_mults.outputs) v2inmul = util.mul_vecs(v2_mults.outputs, inflat) v2 = sum(v2inmul) % Defs.prime assert v1 == finalOutputs[0] assert v2 == sum(finalOutputs) % Defs.prime