def __init__(self, store_clauses=True, store_comments=False): Cadical.__init__(self) self.__var = 1 self.__dbg_clauses = [] self.__dbg_comments = {} self.num_clauses = 0 self.store_clauses = store_clauses self.store_comments = store_comments
def add_clause(self, clause, no_return=True): assert (max(map(abs, clause)) < self.__var) if self.store_clauses: self.__dbg_clauses.append(clause) self.num_clauses += 1 cl = clause.copy() if self.clock_act is not None: cl.append(-self.clock_act) Cadical.add_clause(self, cl, no_return)
def calc_avg_sensitivity(model, n=100): solver = Cadical(bootstrap_with=model['clauses']) s = 0 for i in range(n): input_vals = [ model['net_map'][inp] * sample([-1, 1], 1)[0] for inp in model['inputs'] ] solver.solve(assumptions=input_vals) m = solver.get_model() s += sum( [m[model['net_map'][o] - 1] < 0 for o in model['flip_outputs']]) / len(model['inputs']) model['avg_sensitivity'] = s / n
def construct_solver(c, assumptions=None, engine="cadical"): """ Constructs a SAT solver instance with the given circuit and assumptions Parameters ---------- c : Circuit circuit to encode assumptions : dict of str:int Assumptions to add to solver Returns ------- solver : pysat.Cadical SAT solver instance variables : pysat.IDPool solver variable mapping """ formula, variables = cnf(c) if assumptions: for n in assumptions.keys(): if n not in c: raise ValueError(f"incorrect assumption key: {n}") add_assumptions(formula, variables, assumptions) if engine == "cadical": solver = Cadical(bootstrap_with=formula) elif engine == "glucose": solver = Glucose4(bootstrap_with=formula, incr=True) elif engine == "lingeling": solver = Lingeling(bootstrap_with=formula) else: raise ValueError(f"unsupported solver: {engine}") return solver, variables
def calc_sensitivity(model): n_eq = 0 it = ITotalizer(lits=[model['net_map'][n] for n in model['flip_outputs']], top_id=len(model['net_map']), ubound=len(model['inputs'])) solver = Cadical(bootstrap_with=model['clauses'] + it.cnf.clauses) while True: if solver.solve(assumptions=[-it.rhs[n_eq]]): break else: n_eq += 1 if n_eq == len(model['inputs']): print('0 sensitivity reached') exit() model['sensitivity'] = (len(model['inputs']) - n_eq) / len(model['inputs'])
def checkModelQBF(clauses, universals, existentials, assumptions=[]): assumptions_set = set(assumptions) clauses_reduced = [ reduceClause(clause, assumptions_set) for clause in clauses if not set(clause).intersection(assumptions_set) ] logging.debug( "Model encoding under assumptions: {}".format(clauses_reduced)) variables = {abs(l) for clause in clauses_reduced for l in clause} assert variables <= set(universals).union(existentials), "free variables" result, certificate = solve2QBF(clauses_reduced, universals, existentials) if not result: logging.debug("CADET UNSAT certificate: {}".format(certificate)) solver = Cadical(bootstrap_with=clauses_reduced) assert not solver.solve(certificate), "Certification of UNSAT failed." return result, certificate
def optimal_comb(api: GBD, args): result = api.query_search(args.query, [], args.runtimes) result = [[ int(float(val)) if is_number(val) and float(val) < float(args.tlim) else int(2 * args.tlim) for val in row[1:] ] for row in result] cnf = dimacs.DIMACSPrinter() _ACT = [cnf.create_literal() for _ in range(0, len(args.runtimes))] _MAX = get_bitvector(cnf, int(pow(2, _BW - 1) - 1)) for c in encode_at_most_k_constraint_ltseq(cnf, args.size, _ACT): cnf.consume_clause(c) # encode row-wise minima MINS = [] for row in result: i = 0 B0_ = get_bitvector(cnf, int(row[0])) B0 = if_then_else(cnf, B0_, _MAX, _ACT[i]) for rt in row[1:]: i = i + 1 B1_ = get_bitvector(cnf, int(rt)) B1 = if_then_else(cnf, B1_, _MAX, _ACT[i]) Bcarry = get_carry_bits(cnf, B1, [-i for i in B0]) Bmin = if_then_else(cnf, B0, B1, Bcarry[_BW - 1]) B0 = Bmin MINS.append(B0) # B0 is now minimum of row # encode sum of minima A = MINS[0] for B in MINS[1:]: SUM = get_sum_bits(cnf, A, B) A = SUM solver = Cadical(bootstrap_with=cnf.clauses, with_proof=False) result = solver.solve() if result == True: model = solver.get_model() print(slice_model(model, _ACT)) print(decode_bitvector(slice_model(model, A)))
def construct_solver(c, assumptions=None): """ Constructs a SAT solver instance with the given circuit and assumptions Parameters ---------- c : Circuit circuit to encode assumptions : dict of str:int Assumptions to add to solver Returns ------- solver : pysat.Cadical SAT solver instance variables : pysat.IDPool solver variable mapping """ formula, variables = cnf(c) if assumptions: add_assumptions(formula, variables, assumptions) solver = Cadical(bootstrap_with=formula) return solver, variables
def checkModel(universals_variables, dependency_dict, matrix,model,model_clauses,check_defined,check_consistency,check_dependencies,use_extended_dependencies) : """ Checks if the canditate-model given by <filename_model> certifies the trueness of the DQBF given by <filename_formula>. Parameters ---------- universals_variables : List The universal variables appearing in the formula. dependency_dict : Dictionary Maps each existential variable to its dependencies. matrix : List The matrix of the formula of interest. model : Dictionary Maps each existential variable to a list of clauses (the clausal representation of the model for the variable) model_clauses : List Contains all clauses appearing in some model. check_defined : bool If true, check if each existential variable has a definition. check_consistency : bool If true check for each existential variable if the model clauses are consistent. check_dependencies : bool If true check if the model clauses only use variables according to the dependencies. use_extended_dependencies : bool If true extended dependencies are used for the depdendency check. The extended dependencies consist of the "normal" dependencies plus the existential variables whose dependencies are contained. Returns ------- True, if the given candidate-model passes all checks. """ existential_variables = list(dependency_dict) if check_dependencies : if use_extended_dependencies: dependencies=computeExtendedDependencies(dependency_dict) else: dependencies=dependency_dict variables_to_consider=universals_variables + existential_variables for e in existential_variables: if e in model: #The candidate-model may contain additional variables as those contained in the matrix. #Thus the check shall pass if additional variables occur in the candidate-model -- #as long as those variables do not occur in the matrix variablesOK, false_variables = check_occuring_variables(model[e],variables_to_consider,dependencies[e]+[e]) if not variablesOK: print("The given model for variable {0} contains the invalid variables: {1}.".format(e,false_variables)) return False #Additional sanity check. If the candidate-model is UNSAT it is necessarily inconsistent. #Thus it does not certify that the given DQBF is true. model_checker = Cadical(bootstrap_with=model_clauses) if not model_checker.solve() : print("Model inconsistent") return False if check_consistency : consistent = consistency_checker(model_clauses,universals_variables,existential_variables) if not consistent : print("Model inconsistent") # print("Certificate: {}".format(certificate)) return False if check_defined: definability_checker = DefinabilityChecker(model_clauses, existential_variables) for e in existential_variables : depends_on = dependencies[e] is_defined, counterexample = definability_checker.checkDefinability(depends_on, e) if not is_defined: print("The model does not uniquely define variable: {}".format(e)) return False model_ok = check_matrix(model_checker,matrix) if model_ok: return True else: model = model_checker.get_model() print("Universal assignment: {}".format([l for l in model if abs(l) in universals_variables])) print("Existential assignment: {}".format([l for l in model if abs(l) in existential_variables])) auxiliary_variables_in_model={abs(l) for clause in model_clauses for l in clause if (not abs(l) in set(universals_variables)) and (not abs(l) in set(existential_variables))} print("Auxiliary assignment: {}".format([l for l in model if abs(l) in auxiliary_variables_in_model])) return model_ok
def lebl(c, bw, ng): """ Locks a circuitgraph with Logic-Enhanced Banyan Locking as outlined in Joseph Sweeney, Marijn J.H. Heule, and Lawrence Pileggi Modeling Techniques for Logic Locking. In Proceedings of the International Conference on Computer Aided Design 2020 (ICCAD-39). Parameters ---------- circuit: circuitgraph.CircuitGraph Circuit to lock. bw: int Width of Banyan network. lw: int Minimum number of gates mapped to network. Returns ------- circuitgraph.CircuitGraph, dict of str:bool the locked circuit and the correct key value for each key input """ # create copy to lock cl = cg.copy(c) # generate switch and mux s = cg.Circuit(name='switch') m2 = cg.strip_io(logic.mux(2)) s.extend(cg.relabel(m2, {n: f'm2_0_{n}' for n in m2.nodes()})) s.extend(cg.relabel(m2, {n: f'm2_1_{n}' for n in m2.nodes()})) m4 = cg.strip_io(logic.mux(4)) s.extend(cg.relabel(m4, {n: f'm4_0_{n}' for n in m4.nodes()})) s.extend(cg.relabel(m4, {n: f'm4_1_{n}' for n in m4.nodes()})) s.add('in_0', 'buf', fanout=['m2_0_in_0', 'm2_1_in_1']) s.add('in_1', 'buf', fanout=['m2_0_in_1', 'm2_1_in_0']) s.add('out_0', 'buf', fanin='m4_0_out') s.add('out_1', 'buf', fanin='m4_1_out') s.add('key_0', 'input', fanout=['m2_0_sel_0', 'm2_1_sel_0']) s.add('key_1', 'input', fanout=['m4_0_sel_0', 'm4_1_sel_0']) s.add('key_2', 'input', fanout=['m4_0_sel_1', 'm4_1_sel_1']) # generate banyan I = int(2 * cg.clog2(bw) - 2) J = int(bw / 2) # add switches and muxes for i in range(I * J): cl.extend(cg.relabel(s, {n: f'swb_{i}_{n}' for n in s})) # 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_banyan(cl, swb_ins, swb_outs, bw) # get banyan io net_ins = swb_ins[:bw] net_outs = swb_outs[-bw:] # generate key key = { f'swb_{i//3}_key_{i%3}': choice([True, False]) for i in range(3 * I * J) } # generate connections between banyan nodes bfi = {n: set() for n in swb_outs + net_ins} bfo = {n: set() for n in swb_outs + net_ins} for n in swb_outs + net_ins: if cl.fanout(n): fo_node = cl.fanout(n).pop() swb_i = fo_node.split('_')[1] bfi[f'swb_{swb_i}_out_0'].add(n) bfi[f'swb_{swb_i}_out_1'].add(n) bfo[n].add(f'swb_{swb_i}_out_0') bfo[n].add(f'swb_{swb_i}_out_1') # find a mapping of circuit onto banyan net_map = IDPool() for bn in swb_outs + net_ins: for cn in c: net_map.id(f'm_{bn}_{cn}') # mapping implications clauses = [] for bn in swb_outs + net_ins: # fanin if bfi[bn]: for cn in c: if c.fanin(cn): for fcn in c.fanin(cn): clause = [-net_map.id(f'm_{bn}_{cn}')] clause += [ net_map.id(f'm_{fbn}_{fcn}') for fbn in bfi[bn] ] clause += [ net_map.id(f'm_{fbn}_{cn}') for fbn in bfi[bn] ] clauses.append(clause) else: clause = [-net_map.id(f'm_{bn}_{cn}')] clause += [net_map.id(f'm_{fbn}_{cn}') for fbn in bfi[bn]] clauses.append(clause) # fanout if bfo[bn]: for cn in c: clause = [-net_map.id(f'm_{bn}_{cn}')] clause += [net_map.id(f'm_{fbn}_{cn}') for fbn in bfo[bn]] for fcn in c.fanout(cn): clause += [net_map.id(f'm_{fbn}_{fcn}') for fbn in bfo[bn]] clauses.append(clause) # no feed through for cn in c: net_map.id(f'INPUT_OR_{cn}') net_map.id(f'OUTPUT_OR_{cn}') clauses.append([-net_map.id(f'INPUT_OR_{cn}')] + [net_map.id(f'm_{bn}_{cn}') for bn in net_ins]) clauses.append([-net_map.id(f'OUTPUT_OR_{cn}')] + [net_map.id(f'm_{bn}_{cn}') for bn in net_outs]) for bn in net_ins: clauses.append( [net_map.id(f'INPUT_OR_{cn}'), -net_map.id(f'm_{bn}_{cn}')]) for bn in net_outs: clauses.append( [net_map.id(f'OUTPUT_OR_{cn}'), -net_map.id(f'm_{bn}_{cn}')]) clauses.append( [-net_map.id(f'OUTPUT_OR_{cn}'), -net_map.id(f'INPUT_OR_{cn}')]) # at least ngates for bn in swb_outs + net_ins: net_map.id(f'NGATES_OR_{bn}') clauses.append([-net_map.id(f'NGATES_OR_{bn}')] + [net_map.id(f'm_{bn}_{cn}') for cn in c]) for cn in c: clauses.append( [net_map.id(f'NGATES_OR_{bn}'), -net_map.id(f'm_{bn}_{cn}')]) clauses += CardEnc.atleast( bound=ng, lits=[net_map.id(f'NGATES_OR_{bn}') for bn in swb_outs + net_ins], vpool=net_map).clauses # at most one mapping per out for bn in swb_outs + net_ins: clauses += CardEnc.atmost(lits=[ net_map.id(f'm_{bn}_{cn}') for cn in c ], vpool=net_map).clauses # limit number of times a gate is mapped to net outputs to fanout of gate for cn in c: lits = [net_map.id(f'm_{bn}_{cn}') for bn in net_outs] bound = len(c.fanout(cn)) if len(lits) < bound: continue clauses += CardEnc.atmost(bound=bound, lits=lits, vpool=net_map).clauses # prohibit outputs from net for bn in swb_outs + net_ins: for cn in c.outputs(): clauses += [[-net_map.id(f'm_{bn}_{cn}')]] # solve solver = Cadical(bootstrap_with=clauses) if not solver.solve(): print(f'no config for width: {bw}') core = solver.get_core() print(core) code.interact(local=dict(globals(), **locals())) model = solver.get_model() # get mapping mapping = {} for bn in swb_outs + net_ins: selected_gates = [ cn for cn in c if model[net_map.id(f'm_{bn}_{cn}') - 1] > 0 ] if len(selected_gates) > 1: print(f'multiple gates mapped to: {bn}') code.interact(local=dict(globals(), **locals())) mapping[bn] = selected_gates[0] if selected_gates else None potential_net_fanins = list(c.nodes() - (c.endpoints() | set(mapping.values()) | mapping.keys() | c.startpoints())) # connect net inputs for bn in net_ins: if mapping[bn]: cl.connect(mapping[bn], bn) else: cl.connect(choice(potential_net_fanins), bn) mapping.update({cl.fanin(bn).pop(): cl.fanin(bn).pop() for bn in net_ins}) potential_net_fanouts = list(c.nodes() - (c.startpoints() | set(mapping.values()) | mapping.keys() | c.endpoints())) #selected_fo = {} # connect switch boxes for i, bn in enumerate(swb_outs): # get keys if key[f'swb_{i//2}_key_1'] and key[f'swb_{i//2}_key_2']: k = 3 elif not key[f'swb_{i//2}_key_1'] and key[f'swb_{i//2}_key_2']: k = 2 elif key[f'swb_{i//2}_key_1'] and not key[f'swb_{i//2}_key_2']: k = 1 elif not key[f'swb_{i//2}_key_1'] and not key[f'swb_{i//2}_key_2']: k = 0 switch_key = 1 if key[f'swb_{i//2}_key_0'] == 1 else 0 mux_input = f'swb_{i//2}_m4_{i%2}_in_{k}' # connect inner nodes mux_gate_types = set() # constant output, hookup to a node that is already in the affected outputs fanin, not in others if not mapping[bn] and bn in net_outs: decoy_fanout_gate = choice(potential_net_fanouts) #selected_fo[bn] = decoy_fanout_gate cl.connect(bn, decoy_fanout_gate) if cl.type(decoy_fanout_gate) in ['and', 'nand']: cl.set_type(mux_input, '1') elif cl.type(decoy_fanout_gate) in ['or', 'nor', 'xor', 'xnor']: cl.set_type(mux_input, '0') elif cl.type(decoy_fanout_gate) in ['buf']: if randint(0, 1): cl.set_type(mux_input, '1') cl.set_type(decoy_fanout_gate, choice(['and', 'xnor'])) else: cl.set_type(mux_input, '0') cl.set_type(decoy_fanout_gate, choice(['or', 'xor'])) elif cl.type(decoy_fanout_gate) in ['not']: if randint(0, 1): cl.set_type(mux_input, '1') cl.set_type(decoy_fanout_gate, choice(['nand', 'xor'])) else: cl.set_type(mux_input, '0') cl.set_type(decoy_fanout_gate, choice(['nor', 'xnor'])) elif cl.type(decoy_fanout_gate) in ['0', '1']: cl.set_type(mux_input, cl.type(decoy_fanout_gate)) cl.set_type(decoy_fanout_gate, 'buf') else: print('gate error') code.interact(local=dict(globals(), **locals())) mux_gate_types.add(cl.type(mux_input)) # feedthrough elif mapping[bn] in [mapping[fbn] for fbn in bfi[bn]]: cl.set_type(mux_input, 'buf') mux_gate_types.add('buf') if mapping[cl.fanin(f'swb_{i//2}_in_0').pop()] == mapping[bn]: cl.connect(f'swb_{i//2}_m2_{switch_key}_out', mux_input) else: cl.connect(f'swb_{i//2}_m2_{1-switch_key}_out', mux_input) # gate elif mapping[bn]: cl.set_type(mux_input, cl.type(mapping[bn])) mux_gate_types.add(cl.type(mapping[bn])) gfi = cl.fanin(mapping[bn]) if mapping[cl.fanin(f'swb_{i//2}_in_0').pop()] in gfi: cl.connect(f'swb_{i//2}_m2_{switch_key}_out', mux_input) gfi.remove(mapping[cl.fanin(f'swb_{i//2}_in_0').pop()]) if mapping[cl.fanin(f'swb_{i//2}_in_1').pop()] in gfi: cl.connect(f'swb_{i//2}_m2_{1-switch_key}_out', mux_input) # mapped to None, any key works else: k = None # fill out random gates for j in range(4): if j != k: t = sample( set([ 'buf', 'or', 'nor', 'and', 'nand', 'not', 'xor', 'xnor', '0', '1' ]) - mux_gate_types, 1)[0] mux_gate_types.add(t) mux_input = f'swb_{i//2}_m4_{i%2}_in_{j}' cl.set_type(mux_input, t) if t == 'not' or t == 'buf': # pick a random fanin cl.connect(f'swb_{i//2}_m2_{randint(0,1)}_out', mux_input) elif t == '1' or t == '0': pass else: cl.connect(f'swb_{i//2}_m2_0_out', mux_input) cl.connect(f'swb_{i//2}_m2_1_out', mux_input) if [ n for n in cl if cl.type(n) in ['buf', 'not'] and len(cl.fanin(n)) > 1 ]: import code code.interact(local=dict(globals(), **locals())) # connect outputs non constant outs rev_mapping = {} for bn in net_outs: if mapping[bn]: if mapping[bn] not in rev_mapping: rev_mapping[mapping[bn]] = set() rev_mapping[mapping[bn]].add(bn) for cn in rev_mapping.keys(): #for fcn in cl.fanout(cn): # cl.connect(sample(rev_mapping[cn],1)[0],fcn) for fcn, bn in zip_longest(cl.fanout(cn), rev_mapping[cn], fillvalue=list(rev_mapping[cn])[0]): cl.connect(bn, fcn) # delete mapped gates deleted = True while deleted: deleted = False for n in cl.nodes(): # node and all fanout are in the net if n not in mapping and n in mapping.values(): if all(s not in mapping and s in mapping.values() for s in cl.fanout(n)): cl.remove(n) deleted = True # node in net fanout if n in [mapping[o] for o in net_outs] and n in cl: cl.remove(n) deleted = True cg.lint(cl) return cl, key
def test_as_DIMACS_CNF(self): for index, g in enumerate(self.games): cond = g.get_cnf_solution() solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond))) self.assertTrue(solver.solve())
def test_condition_three_clauses(self): for index, g in enumerate(self.games): cond = g.condition_three_clauses() solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond))) self.assertTrue(solver.solve())
def test_condition_one_clauses(self): for index, g in enumerate(self.games): cond = g.condition_one_clauses() self.assertLessEqual(len(cond), 8 * g.capacity) solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond))) self.assertTrue(solver.solve())
def analyse_sat_solvers(games: [GameEncoder], show_png=False): from timeit import default_timer as timer from pysat.solvers import Cadical, Glucose4, Lingeling, Minisat22, Maplesat df = pd.DataFrame(columns=["Solver", "Execution time [sec]", "Algorithm"]) for g in games: cnf_ = CNF(from_string=as_DIMACS_CNF(g.get_cnf_solution())) solvers = { "Cadical": Cadical(cnf_), "Glucose4": Glucose4(cnf_), "Lingeling": Lingeling(cnf_), "Minisat22": Minisat22(cnf_), "Maplesat": Maplesat(cnf_) } for name, solver in solvers.items(): start = timer() solved = solver.solve() end = timer() delta_t = end - start print(name, "\tSolved:", solved, "\tTime:", delta_t) df = df.append( { "Solver": name, "Execution time [sec]": delta_t, "Algorithm": g.algo_name }, ignore_index=True) df.to_csv("data\\solver_analysis.csv", index=False) print("Saved data-frame as csv.") plt.yscale('log') sns.barplot(x="Solver", y="Execution time [sec]", hue="Algorithm", data=df) plt.savefig("data\\solver_performance_analysis.png") if show_png: print("Plotting graph...") plt.show() class EncodingPerformanceAnalysis: def __init__(self, efficient=True): print( f"EFFICIENT={efficient}: Creating and solving multiple games, this might take a while..." ) paths = glob.glob("tent-inputs\\*.txt") self.efficient = efficient self.games = [ GameEncoderBinomial.from_text_file(path, efficient=self.efficient, verbose=False) for path in paths ] self.games_cnf = [g.get_cnf_solution() for g in self.games] def store_metrics(self): df = pd.DataFrame(columns=[ "Field-size", "Literals", "Variables", "Clauses", "Algorithm" ]) for index, cnf in enumerate(self.games_cnf): game_size = self.games[index].capacity clauses_n = len(cnf) literals = list(chain(*cnf)) variables = set(abs(x) for x in literals) df = df.append( { "Algorithm": "Efficient" if self.efficient else "Simple", "Clauses": clauses_n, "Field-size": game_size, "Literals": len(literals), "Variables": len(variables) }, ignore_index=True) df.Capacity = df.Capacity.astype(int) df.Literals = df.Literals.astype(int) df.Variables = df.Variables.astype(int) time_id = datetime.datetime.now().strftime('%m-%d_%H-%M-%S') df.to_csv(f"data\\encoding_performance_analysis_{time_id}.csv", index=False) print("Saved data-frame as csv.")
def get_solution(conditions): cnf_string = as_DIMACS_CNF(conditions) solver = Cadical(CNF(from_string=cnf_string)) solved, solution = solver.solve(), solver.get_model() return solved, solution
def solve_instance(cnf): with Cadical(CNF(from_file=cnf), use_timer=True) as solver: ans = solver.solve() return cnf.stem, ans, solver.status, solver.get_model( ), solver.get_core(), solver.nof_vars(), solver.time()
c.add_edge(inp_con, f'flip_{inp}_{inp_con}') # add output xnor c.add_node(f'flip_{inp}_output_xnor', gate='xnor') c.add_edge(f'flip_{inp}_out', f'flip_{inp}_output_xnor') c.add_edge('out', f'flip_{inp}_output_xnor') flip_outputs.append(f'flip_{inp}_output_xnor') # get dimacs clauses, net_map = gates2dimacs(c) # encode sensitivity value it = ITotalizer(lits=[net_map[n] for n in flip_outputs], top_id=len(net_map), ubound=len(protected_inputs)) solver = Cadical(bootstrap_with=clauses + it.cnf.clauses) # find max sensitivity input, check n_eq = 0 while n_eq < len(protected_inputs): if solver.solve(assumptions=[-it.rhs[n_eq]]): # get input output m = solver.get_model() inp = {i: m[net_map[i] - 1] > 0 for i in protected_inputs} s = (len(protected_inputs) - n_eq) / len(protected_inputs) print(f'found input w/ sensitivity {s}') # check input against key if all(inp[f'protected_in_{i}_'] == k for i, k in enumerate(key)): sensitivity = (len(protected_inputs) - n_eq) / len(protected_inputs)
def add_clause(self, clause, no_return=True): assert (max(map(abs, clause)) < self.__var) if self.store_clauses: self.__dbg_clauses.append(clause) self.num_clauses += 1 Cadical.add_clause(self, clause, no_return)