def miter(c0, c1=None, startpoints=None, endpoints=None): """ Creates a miter circuit Parameters ---------- c0 : Circuit First circuit. c1 : Circuit Optional second circuit, if None c0 is mitered with itself. startpoints : set of str Nodes to be tied together, must exist in both circuits. endpoints : set of str Nodes to be compared, must exist in both circuits. Returns ------- Circuit Miter circuit. """ # check for blackboxes if c0.blackboxes: raise ValueError(f"{c0.name} contains a blackbox") if c1 and c1.blackboxes: raise ValueError(f"{c1.name} contains a blackbox") # clean inputs if not c1: c1 = c0 if not startpoints: startpoints = c0.startpoints() & c1.startpoints() if not endpoints: endpoints = c0.endpoints() & c1.endpoints() # create miter, relabel m = Circuit(name=f"miter_{c0.name}_{c1.name}") m.add_subcircuit(c0, "c0") m.add_subcircuit(c1, "c1") # tie inputs for n in startpoints: m.add(n, "input", fanout=[f"c0_{n}", f"c1_{n}"]) # compare outputs m.add("miter", "or") m.add("sat", "output", fanin="miter") for n in endpoints: m.add(f"dif_{n}", "xor", fanin=[f"c0_{n}", f"c1_{n}"], fanout="miter") return m
def popcount(w): """ Create a population count circuit. Parameters ---------- w : int Input width of the circuit. Returns ------- Circuit Population count circuit. """ c = Circuit(name="popcount") ps = [[c.add(f"in_{i}", "input")] for i in range(w)] c.add("tie0", "0") i = 0 while len(ps) > 1: # get values ns = ps.pop(0) ms = ps.pop(0) # pad aw = max(len(ns), len(ms)) while len(ms) < aw: ms += ["tie0"] while len(ns) < aw: ns += ["tie0"] # instantiate and connect adder c.add_subcircuit(adder(aw), f"add_{i}") for j, (n, m) in enumerate(zip(ns, ms)): c.connect(n, f"add_{i}_a_{j}") c.connect(m, f"add_{i}_b_{j}") # add adder outputs ps.append([f"add_{i}_out_{j}" for j in range(aw + 1)]) i += 1 # connect outputs for i, o in enumerate(ps[0]): c.add(f"out_{i}", "output", fanin=o) if not c.fanout("tie0"): c.remove("tie0") return c
def influence_transform(c, n, s): """ Creates a circuit to compute sensitivity. Parameters ---------- c : Circuit Sequential circuit to unroll. n : str Node to compute influence at. s : str Startpoint to compute influence for. Returns ------- Circuit Influence circuit. """ # check for blackboxes if c.blackboxes: raise ValueError(f"{c.name} contains a blackbox") # check if s is in startpoints sp = c.startpoints(n) if s not in sp: raise ValueError(f"{s} is not in startpoints of {n}") # get input cone fi_nodes = c.transitive_fanin(n) | set([n]) sub_c = Circuit("sub_cone", c.graph.subgraph(fi_nodes).copy()) # create two copies of sub circuit, share inputs except s infl = Circuit(name=f"infl_{s}_on_{n}") infl.add_subcircuit(sub_c, "c0") infl.add_subcircuit(sub_c, "c1") for g in sp: if g != s: infl.add(g, "input", fanout=[f"c0_{g}", f"c1_{g}"]) else: infl.add(f"not_{g}", "not", fanout=f"c1_{s}") infl.add(g, "input", fanout=[f"c0_{g}", f"not_{g}"]) infl.add("dif", "xor", fanin=[f"c0_{n}", f"c1_{n}"]) infl.add("sat", "output", fanin="dif") return infl
def sensitivity_transform(c, n): """ Creates a circuit to compute sensitivity. Parameters ---------- c : Circuit Sequential circuit to unroll. n : str Node to compute sensitivity at. Returns ------- Circuit Sensitivity circuit. """ # check for blackboxes if c.blackboxes: raise ValueError(f"{c.name} contains a blackbox") # check for startpoints startpoints = c.startpoints(n) if len(startpoints) < 1: raise ValueError(f"{n} has no startpoints") # get input cone fi_nodes = c.transitive_fanin(n) | set([n]) sub_c = Circuit(graph=c.graph.subgraph(fi_nodes).copy()) # create sensitivity circuit sen = Circuit() sen.add_subcircuit(sub_c, "orig") for s in startpoints: sen.add(s, "input", fanout=f"orig_{s}") # add popcount sen.add_subcircuit(popcount(len(startpoints)), "pc") # add inverted input copies for i, s0 in enumerate(startpoints): sen.add_subcircuit(sub_c, f"inv_{s0}") # connect inputs for s1 in startpoints: if s0 != s1: sen.connect(s1, f"inv_{s0}_{s1}") else: # connect inverted input sen.set_type(f"inv_{s0}_{s1}", "not") sen.connect(s0, f"inv_{s0}_{s1}") # compare to orig sen.add( f"dif_{s0}", "xor", fanin=[f"orig_{n}", f"inv_{s0}_{n}"], fanout=f"pc_in_{i}", ) sen.add(f"dif_out_{s0}", "output", fanin=f"dif_{s0}") # instantiate population count for o in range(clog2(len(startpoints) + 1)): sen.add(f"sen_out_{o}", "output", fanin=f"pc_out_{o}") return sen
def banyan(bw): """ Create a Banyan switching network Parameters ---------- bw : int Input/output width of the network. Returns ------- Circuit Network circuit. """ b = Circuit() # generate switch m = mux(2) s = Circuit(name="switch") s.add_subcircuit(m, f"m0") s.add_subcircuit(m, f"m1") s.add("in_0", "buf", fanout=["m0_in_0", "m1_in_1"]) s.add("in_1", "buf", fanout=["m0_in_1", "m1_in_0"]) s.add("out_0", "buf", fanin="m0_out") s.add("out_1", "buf", fanin="m1_out") s.add("sel", "input", fanout=["m0_sel_0", "m1_sel_0"]) # generate banyan I = int(2 * clog2(bw) - 2) J = int(bw / 2) # add switches for i in range(I * J): b.add_subcircuit(s, f"swb_{i}") # make connections swb_ins = [f"swb_{i//2}_in_{i%2}" for i in range(I * J * 2)] swb_outs = [f"swb_{i//2}_out_{i%2}" for i in range(I * J * 2)] # connect switches for i in range(clog2(J)): r = J / (2**i) for j in range(J): t = (j % r) >= (r / 2) # straight out_i = int((i * bw) + (2 * j) + t) in_i = int((i * bw + bw) + (2 * j) + t) b.connect(swb_outs[out_i], swb_ins[in_i]) # cross out_i = int((i * bw) + (2 * j) + (1 - t) + ((r - 1) * ((1 - t) * 2 - 1))) in_i = int((i * bw + bw) + (2 * j) + (1 - t)) b.connect(swb_outs[out_i], swb_ins[in_i]) if r > 2: # straight out_i = int(((I * J * 2) - ((2 + i) * bw)) + (2 * j) + t) in_i = int(((I * J * 2) - ((1 + i) * bw)) + (2 * j) + t) b.connect(swb_outs[out_i], swb_ins[in_i]) # cross out_i = int(((I * J * 2) - ((2 + i) * bw)) + (2 * j) + (1 - t) + ((r - 1) * ((1 - t) * 2 - 1))) in_i = int(((I * J * 2) - ((1 + i) * bw)) + (2 * j) + (1 - t)) b.connect(swb_outs[out_i], swb_ins[in_i]) # create banyan io net_ins = swb_ins[:bw] net_outs = swb_outs[-bw:] for i, net_in in enumerate(net_ins): b.add(f"in_{i}", "input", fanout=net_in) for i, net_out in enumerate(net_outs): b.add(f"out_{i}", "output", fanin=net_out) for i in range(I * J): b.add(f"sel_{i}", "input", fanout=f"swb_{i}_sel") return b