def int2binlatch(varlist, n): net = boolnet.BoolNet(True) dividend = n power = len(varlist) - 1 for v in varlist: divisor = math.pow(2, power) if dividend >= divisor: net &= boolnet.BoolNet(v) dividend -= divisor else: net &= ~boolnet.BoolNet(v) power -= 1 return net
def label2inputs(inputs, outputs, label, input_map): all_signals = inputs + outputs labels = label.split("||") input_net = boolnet.BoolNet(False) for l in labels: (props, n_disj) = utils.convert_formula_to_proptab(l, all_signals) if props == "T": DBG_MSG("Trivial edge") input_net |= boolnet.BoolNet(True) continue props = list(props) temp = boolnet.BoolNet(True) for p in range(len(all_signals)): p_val = props[p] if p_val == "1": temp &= boolnet.BoolNet(input_map[all_signals[p]]) elif p_val == "2": temp &= ~boolnet.BoolNet(input_map[all_signals[p]]) input_net |= temp return input_net
def main(formula_file, part_file, k, args): # STEP 0: read partition, ltl formula and create BA (inputs, outputs) = read_partition(part_file) wring_formulae = read_formulae(formula_file, args.compositional) var_offset = None latch_net = dict() error_net = boolnet.BoolNet(False) for wring_formula in wring_formulae: ltl2ba_formula = wring_to_ltl2ba(wring_formula, inputs, outputs) formula = negate_ltl2ba(ltl2ba_formula) DBG_MSG("negated formula: " + str(formula)) (automata, buchi_states) = construct_automata(formula) # STEP 1: translate aig (ln, en, var_offset) = translate2aig(inputs, outputs, k, automata.nodes(), buchi_states, var_offset, [(e, automata.edge_label(e)) for e in automata.edges()]) latch_net.update(ln) error_net |= en # STEP 2: call Acacia+ to see if this is realizable or not arg_list = [ "--ltl", formula_file, "--part", part_file, "--player", "1", "--kbound", str(k - 1), "--verb", "0", "--crit", "OFF", "--opt", "none", "--check", "REAL" ] if args.compositional: arg_list.extend(["--syn", "COMP", "--nbw", "COMP"]) (solved, is_real) = acacia_plus.main(arg_list) LOG_MSG("acacia+ replied (solved, realizability) = (" + str(solved) + ", " + str(is_real) + ")") suffix = "comp" + str(k) if args.compositional else str(k) if solved and is_real: file_name = formula_file[:-4] + "_" + suffix + "_REAL.aag" ret = EXIT_STATUS_REALIZABLE elif solved and not is_real: file_name = formula_file[:-4] + "_" + suffix + "_UNREAL.aag" ret = EXIT_STATUS_UNREALIZABLE else: file_name = formula_file[:-4] + "_" + suffix + "_UNREAL.aag" ret = EXIT_STATUS_UNKNOWN # FINALLY: dump the AIG write_aig(inputs, outputs, latch_net, error_net, file_name) return ret
def translate2aig(inputs, outputs, k, states, buchi_states, var_offset, edges): LOG_MSG("k = " + str(k)) LOG_MSG(str(len(inputs)) + " inputs") DBG_MSG("inputs: " + str(inputs)) LOG_MSG(str(len(outputs)) + " outputs") DBG_MSG("outputs: " + str(outputs)) # STEP 1: check number of states n_nodes = len(states) LOG_MSG(str(n_nodes) + " states") DBG_MSG("states: " + str(states)) # STEP 2: assign inputs and outputs a number free_var = 2 input_map = dict() input_map["F"] = 0 input_map["T"] = 1 for i in inputs: input_map[i] = free_var free_var += 2 # reserve outputs and negations for o in outputs: input_map[o] = free_var free_var += 2 # reserve latches X counters, and negations # and get the initial node if var_offset is not None: assert free_var <= var_offset free_var = var_offset state_latch_map = dict() latch_net = dict() init_node = None for u in states: if u == "initial": init_node = u DBG_MSG("initial state: " + str(u)) for i in range(k + 2): state_latch_map[(u, i)] = free_var latch_net[free_var] = boolnet.BoolNet(False) free_var += 2 LOG_MSG(str(len(buchi_states)) + " buchi states") DBG_MSG("buchi states: " + str(buchi_states)) LOG_MSG(str(len(edges)) + " edges") # STEP 3: create the boolean network rep. of automata # first transition is to let the 0 config go directly to the initial state all_off = boolnet.BoolNet(True) for latch in state_latch_map.values(): all_off &= ~boolnet.BoolNet(latch) latch_net[state_latch_map[(init_node, 0)]] |= all_off # now add each individual transition, # incrementing counters when a state is buchi for ((u, v), l) in edges: # state u goes to state v DBG_MSG("edge: " + str(u) + "->" + str(v) + " (label: " + str(l) + ")") # which inputs enable the transition? input_net = label2inputs(inputs, outputs, l, input_map) # play with the counters for i in range(k + 2): # if buchi, add value if v in buchi_states: j = min(i + 1, k + 1) latch_net[state_latch_map[(v, j)]] |= ( boolnet.BoolNet(state_latch_map[(u, i)]) & input_net) else: latch_net[state_latch_map[(v, i)]] |= ( boolnet.BoolNet(state_latch_map[(u, i)]) & input_net) # STEP 4: create the error net error_net = boolnet.BoolNet(False) for u in states: error_net |= boolnet.BoolNet(state_latch_map[(u, k + 1)]) # RETURN latchnet and errornet return (latch_net, error_net, free_var)
def write_aig(inputs, outputs, latches, error, file_name): f = open(file_name, "w") all_signals = inputs + outputs n_signals = len(all_signals) n_latches = len(latches) # STEP 0: Compute the number of gates to be used m_vars = boolnet.BoolNet.count_nonterminals() # STEP 1: Print header f.write("aag " + str(m_vars + n_signals + n_latches) + " " + str(n_signals) + " " + str(n_latches) + " " + "1 " + str(m_vars) + "\n") # STEP 2: Print inputs (and name them) var_map = dict() for i in range(2, 2 * (n_signals + 1), 2): f.write(str(i) + "\n") var_map[boolnet.BoolNet(i).index] = i # STEP 3: Print latches # for this part we need to have numbered/named the gates # and assigned a var to True and False cur_var = 2 * (n_signals + n_latches + 1) var_map[0] = 0 var_map[1] = 1 for v in boolnet.BoolNet.iterate_nonterminals(): var_map[v.index] = cur_var cur_var += 2 # name the latches and print their latch [space] net.index line # TECH NOTE # ========= # the boolnet data structure makes sure the following invariant holds: # v.neg => v is a literal, therefore v.is_or() != v.neg is equivalent to # v.is_or() | v.neg for (l, net) in sorted(latches.items()): var_map[boolnet.BoolNet(l).index] = l for (l, net) in sorted(latches.items()): assert ~net.is_or() | ~net.neg if net.is_or() != net.neg: f.write(str(l) + " " + str(var_map[net.index] ^ 1) + "\n") else: f.write(str(l) + " " + str(var_map[net.index]) + "\n") # STEP 4: Print error err = var_map[error.index] if error.is_or() != error.neg: err ^= 1 f.write(str(err) + "\n") # STEP 5: Print gates # we are using deMorgan's Law to have all gates be AND-gates for v in boolnet.BoolNet.iterate_nonterminals(): f.write(str(var_map[v.index]) + " ") # the gate might be an or, meaning everyone else will use its # negation local_neg = v.is_or() # we now print the left operand left = v.get_left() assert ~left.is_or() | ~left.neg if local_neg != (left.is_or() != left.neg): f.write(str(var_map[left.index] ^ 1) + " ") else: f.write(str(var_map[left.index]) + " ") # same treatment for the right operand right = v.get_right() assert ~right.is_or() | ~right.neg if local_neg != (right.is_or() != right.neg): f.write(str(var_map[right.index] ^ 1) + "\n") else: f.write(str(var_map[right.index]) + "\n") # STEP 6: Print symbol table cnt = 0 for i in inputs: f.write("i" + str(cnt) + " " + str(i) + "\n") cnt += 1 for i in outputs: f.write("i" + str(cnt) + " controllable_" + str(i) + "\n") cnt += 1 cnt = 0 for l in latches: f.write("l" + str(cnt) + " latch" + str(cnt) + "\n") cnt += 1 f.write("o0 error\n") # STEP 7: Close the file f.close()