def normalize(model, seed, sample_count, engine='pa'): if engine == 'pa': solver = PredicateAbstractionEngine(model.domain, model.support, model.weightfun) elif engine == 'rej': solver = RejectionEngine(model.domain, model.support, model.weightfun, sample_count=sample_count, seed=seed) else: raise NotImplementedError() Z = solver.compute_volume() assert(Z >= 0), "Z is negative" if not np.isclose(Z, 1.0): logger.debug("Normalizing w with Z: {}".format(Z)) model.weightfun = Times(Real(1.0/Z), model.weightfun) return Z
def test_adaptive_unweighted_real(): domain = Domain.make([], ["x", "y"], [(-5, 10), (-5, 10)]) x, y = domain.get_symbols(domain.variables) support = (x >= -4) & (x <= y) & (y <= 9) & ((y <= -1) | (y >= 6)) weight = Real(1.0) engine = AdaptiveRejection(domain, support, weight, SAMPLE_COUNT, SAMPLE_COUNT / 10) computed_volume = engine.compute_volume() rejection_engine = RejectionEngine(domain, support, weight, SAMPLE_COUNT) correction_volume_rej = rejection_engine.compute_volume() print(computed_volume, correction_volume_rej, APPROX_ERROR * correction_volume_rej) assert computed_volume == pytest.approx(correction_volume_rej, rel=APPROX_ERROR) query = x <= y / 2 prob_adaptive = engine.compute_probability(query) prob_rej = rejection_engine.compute_probability(query) assert prob_adaptive == pytest.approx(prob_rej, rel=APPROX_ERROR)
def test_msat_back_simple(self): from pysmt.solvers.msat import MathSAT5Solver, MSatConverter env = get_env() msat = MathSAT5Solver(environment=env, logic=QF_UFLIRA) new_converter = MSatConverter(env, msat.msat_env) r, s = FreshSymbol(REAL), FreshSymbol(INT) f1 = GT(r, Real(1)) f2 = LE(Plus(s, Int(2)), Int(3)) f3 = LE(Int(2), Int(3)) f = And(f1, f2, f3) term = new_converter.convert(f) res = new_converter.back(term) # Checking equality is not enough: MathSAT can change the # shape of the formula into a logically equivalent form. self.assertTrue(is_valid(Iff(f, res), logic=QF_UFLIRA))
def test_substitution_on_functions(self): i, r = FreshSymbol(INT), FreshSymbol(REAL) f = Symbol("f", FunctionType(BOOL, [INT, REAL])) phi = Function(f, [Plus(i, Int(1)), Minus(r, Real(2))]) phi_sub = substitute(phi, {i: Int(0)}).simplify() self.assertEqual(phi_sub, Function(f, [Int(1), Minus(r, Real(2))])) phi_sub = substitute(phi, {r: Real(0)}).simplify() self.assertEqual(phi_sub, Function(f, [Plus(i, Int(1)), Real(-2)])) phi_sub = substitute(phi, {r: Real(0), i: Int(0)}).simplify() self.assertEqual(phi_sub, Function(f, [Int(1), Real(-2)]))
def SpinTimes(spin, bias): """Define our own multiplication for bias times spins. This allows for cleaner log code as well as value checking. Args: spin (int): -1 or 1 bias (:class:`pysmt.shortcuts.Symbol`): The bias Returns: spins * bias """ if not isinstance(spin, int): raise TypeError('spin must be an int') if spin == -1: return Times(Real((-1, 1)), bias) # -1 / 1 elif spin == 1: # identity return bias else: raise ValueError('expected spins to be -1., or 1.')
def test_substitution_complex(self): x, y = Symbol("x", REAL), Symbol("y", REAL) # y = 0 /\ (Forall x. x > 3 /\ y < 2) f = And(Equals(y, Real(0)), ForAll([x], And(GT(x, Real(3)), LT(y, Real(2))))) subs = { y: Real(0), ForAll([x], And(GT(x, Real(3)), LT(y, Real(2)))): TRUE() } f_subs = substitute(f, subs).simplify() if self.env.SubstituterClass == MGSubstituter: self.assertEqual(f_subs, TRUE()) else: # In the MSS the y=0 substitution is performed first, # therefore, the overall quantified expression does not # match the one defined in the substitution map. # See test_substitution_complex_mss for a positive example. self.assertEqual(f_subs, ForAll([x], GT(x, Real(3))))
def test_trivial_weight_function_partial(): # Support: (a | b) & (~a | ~b) & (x >= 0) & (x <= y) & (y <= 10) # Weight: 1 domain = Domain.make(["a", "b"], ["x", "y"], [(0, 1), (0, 1)]) a, b, x, y = domain.get_symbols(domain.variables) support = (a | b) & (~a | ~b) & (x >= 0) & (x <= y) & (y <= 1) weight = Real(1.0) computed_volume = XsddEngine( domain=domain, support=support, weight=weight, convex_backend=EngineConvexIntegrationBackend(PyXaddEngine()), ).compute_volume() correction_volume_rej = RejectionEngine(domain, support, weight, 1000000).compute_volume() correction_volume_xadd = PyXaddEngine(domain, support, weight).compute_volume() # print(correction_volume_rej, correction_volume_xadd, computed_volume) assert computed_volume == pytest.approx(correction_volume_rej, rel=ERROR) assert computed_volume == pytest.approx(correction_volume_xadd, rel=ERROR)
def filter_intervals(bounds: List, var, domains, solver_name='msat'): min_bound, max_bound = min(bounds), max(bounds) extended_bounds = bounds + [min_bound - 1, max_bound + 1] # replace inf extended_bounds.sort() intervals = list(zip(extended_bounds, extended_bounds[1:])) res_intervals = [] for start, end in intervals: test_point = (start + end) * 0.5 test = Equals(var, Real(test_point)) problem = domains.And(test) with Solver(name=solver_name) as solver: solver.add_assertion(problem) if solver.solve(): if start < min_bound: res_intervals.append([float('-inf'), end]) elif end > max_bound: res_intervals.append([start, float('inf')]) else: res_intervals.append([start, end]) return res_intervals
def test_propagate_toplevel(self): x = Symbol("x", REAL) y = Symbol("y", REAL) f = And(LT(Real(4), Times(x, x)), Equals(Real(1), x)) fp = propagate_toplevel(f) self.assertTrue(fp.is_false()) if self.env.factory.has_solvers(logic=QF_NRA): try: ok = is_valid(Iff(f, fp)) except SolverReturnedUnknownResultError: ok = not logic.quantifier_free self.assertTrue(ok) f = And(LT(Real(4), Times(x, x)), Equals(y, x), Equals(y, Real(1))) fp = propagate_toplevel(f) self.assertTrue(fp.is_false()) if self.env.factory.has_solvers(logic=QF_NRA): try: ok = is_valid(Iff(f, fp)) except SolverReturnedUnknownResultError: ok = not logic.quantifier_free self.assertTrue(ok) f = And(Equals(Real(4), x), Equals(y, x), Equals(y, Real(0))) fp = propagate_toplevel(f) self.assertTrue(fp.is_false()) fp = propagate_toplevel(f, preserve_equivalence=False) self.assertTrue(fp.is_false()) fp = propagate_toplevel(f, preserve_equivalence=False, do_simplify=False) self.assertTrue(fp.is_false()) f = Equals(Real(4), Real(5)) fp = propagate_toplevel(f, do_simplify=False) self.assertTrue(fp.is_false())
def test_solving_under_assumption_mixed(self): x = Symbol("x", REAL) v1 = GT(x, Real(10)) v2 = Symbol("v2", BOOL) xor = Or(And(v1, Not(v2)), And(Not(v1), v2)) for name in get_env().factory.all_solvers(logic=QF_UFLIRA): with Solver(name=name) as solver: solver.add_assertion(xor) res1 = solver.solve(assumptions=[v1, Not(v2)]) model1 = solver.get_model() res2 = solver.solve(assumptions=[Not(v1), v2]) model2 = solver.get_model() res3 = solver.solve(assumptions=[v1, v2]) self.assertTrue(res1) self.assertTrue(res2) self.assertFalse(res3) self.assertEqual(model1.get_value(v1), TRUE()) self.assertEqual(model1.get_value(v2), FALSE()) self.assertEqual(model2.get_value(v1), FALSE()) self.assertEqual(model2.get_value(v2), TRUE())
def compensating_potentials(self, n_comp_lits): winit = lambda x: safeexp(self.rand_gen.random()) comp_pots = {} init_w = 1 for k in self.relaxations: if len(k) == 1: continue x, y = k xc = self.relaxations[(x,y)]['copy_name'] var_x = self.primal.nodes()[x]['var'] var_xc = self.primal.nodes()[xc]['var'] if x not in comp_pots: # REM model for 'x' rem = MP2WMI(self.primal.get_univariate_formula(x), Real(1), n_processes=self.n_processes) # sampling compensating potentials for 'x' # original = [[self.sample_comp_literal(var_x), winit(None)] # for _ in range(n_comp_lits)] # try uniform weights original = [[self.sample_comp_literal(var_x), init_w] for _ in range(n_comp_lits)] print(f"%%%%%%% var {x} compensating literals:") for lit, w in original: print(f"%%%%%%% {lit}") comp_pots[x] = (rem, original, {}) else: original = comp_pots[x][1] # using the same comp lits for the copies of 'x' # comp_pots[x][2][xc] = [ # [substitute(lit, {var_x: var_xc}), winit(None)] # for lit, _ in original] comp_pots[x][2][xc] = [ [substitute(lit, {var_x: var_xc}), init_w] for lit, _ in original] return comp_pots
def SMT_solver(rule, a, b, feature_num, con_num, data): letters = [] letters1 = [] for i in range(feature_num): letters.append(Symbol('x' + str(i), REAL)) letters1.append(Symbol('x_' + str(i), INT)) domains = And([ And(GE(letters[i], Real(float(a[i]))), LE(letters[i], Real(float(b[i])))) for i in range(feature_num) ]) problem_rule = [] for node in rule: if node[1] == ">": problem_rule.append(GT(letters[node[0]], Real(float(node[2])))) else: problem_rule.append(LE(letters[node[0]], Real(float(node[2])))) problem = And(problem_rule) constraint = And([ And( Implies(Equals(letters[i], Real(float(data[i]))), Equals(letters1[i], Int(0))), Implies(NotEquals(letters[i], Real(float(data[i]))), Equals(letters1[i], Int(1)))) for i in range(feature_num) ]) sum_letters1 = Plus(letters1) problem1 = LE(sum_letters1, Int(con_num)) formula = And([domains, problem, constraint, problem1]) test_case = [] with Solver(name='z3', random_seed=23) as solver: solver.add_assertion(formula) if solver.solve(): for l in letters: ans = solver.get_py_value(l) test_case.append(float(ans)) print("find a solution") # else: # print("No solution found") return test_case
def renormalize(self, support): assert (support is not None), "Can't renormalize with support = None" self.support = support # mark the tree queue = [] for leaf in self.root.get_leaves(): domA = [ var.symbol_name() for var in leaf.bounds if var.symbol_type() == BOOL ] domX = [] bs = [] for var, b in leaf.bounds.items(): if var.symbol_type() == REAL: domX.append(var.symbol_name()) bs.append(tuple(b)) domain = Domain.make(domA, domX, bs) intersection = And(support, leaf.bounds_to_SMT()) engine = PredicateAbstractionEngine(domain, intersection, Real(1)) intervol = engine.compute_volume() leaf.marked = intervol <= 0 if leaf.marked: logger.debug("Marked a leaf") queue.append(leaf) while len(queue) > 0: n = queue.pop(0) if not n.parent.marked: if n.parent.pos.marked and n.parent.neg.marked: n.parent.marked = True queue.append(n.parent) self.root.merge_marked() self.root.renormalize_node(support)
def sympy2pysmt(expression): """Converts a sympy formula representing a polynomial into a pysmt formula. Keyword arguments: expression -- sympy formula. Raises: WMIParsingError -- If it fails to parse the formula. """ if expression.is_Add: return Plus(map(sympy2pysmt, expression.args)) elif expression.is_Mul: return Times(map(sympy2pysmt, expression.args)) elif expression.is_Pow: base, exp = expression.args return Pow(sympy2pysmt(base), sympy2pysmt(exp)) elif expression.is_Symbol: return Symbol(str(expression), REAL) elif expression.is_Number: return Real(float(expression)) else: msg = "Couldn't parse the sympy formula: " + str(expression) raise WMIParsingError(msg, None)
def check_Z_normalize(model, seed, sample_count): """Tests whether the model is normalized. If not, updates the weight function accordingly.""" logger.debug("Approximating Z") solver = RejectionEngine(model.domain, model.support, model.weightfun, sample_count=sample_count, seed=seed) all_ohes = dict() for var in model.domain.bool_vars: print("VAR:", var) if "_OHE_" in var: prefix = var.partition("_OHE_")[0] if prefix not in all_ohes: all_ohes[prefix] = [] all_ohes[prefix].append(var) ohe_variables = list(all_ohes.values()) if len(all_ohes) > 0 else None Z_approx = solver.compute_volume(ohe_variables=ohe_variables) logger.debug("Z_approx: {}".format(Z_approx)) if Z_approx <= 0: raise ModelException("Partition function is <= 0") if not abs(Z_approx - 1.0) <= DEF_CLOSE_ENOUGH: model.weightfun = Times(Real(float(1.0/Z_approx)), model.weightfun)
def test_div_pow(self): x = FreshSymbol(REAL) f = Equals(Times(Real(4), Pow(x, Real(-1))), Real(2)) try: self.assertTrue(is_sat(f)) except SolverReturnedUnknownResultError: pass f = Equals(Div(Real(4), x), Real(2)) try: self.assertTrue(is_sat(f, solver_name="z3")) except SolverReturnedUnknownResultError: pass f = Equals(Times(x, x), Real(16)) try: self.assertTrue(is_sat(f)) except SolverReturnedUnknownResultError: pass
def __init__(self, symbol: FNode, label: int, domains: FNode = None, cache: bool = True, n_bbox: int = 2, xrange: float = 1): """ :param symbol: :param label: :param domains: :param cache: :param n_bbox: the number of disjuctive intervals in domain :param xrange: the range of the whole bounding box, from -xrange to xrange """ self.symbol = symbol self.label = label self.edges = defaultdict(list) # key label to value TEdge self.children = [] # a list of TNode self.domains = domains or And(symbol >= Real(-1), symbol <= Real(1)) if domains: self.domains = domains else: intervals = get_bounding_box(n_bbox, xrange) clauses = [] pre_start, pre_end = intervals[0] clauses.append(LE(Real(pre_start), symbol)) for start, end in intervals[1:]: clauses.append( Or(LE(symbol, Real(pre_end)), LE(Real(start), symbol))) pre_end = end clauses.append(LE(symbol, Real(pre_end))) self.domains = And([c for c in clauses]) self.initiations = [] if cache: self.cache_index = [] self.cache = dict() self.cache_ide = [] # cache intervals and degree
def test_times_one(self): r = Symbol("r", REAL) f = Times(r, r, Real(1)) f = f.simplify() self.assertNotIn(Real(1), f.args())
def get_full_example_formulae(environment=None): """Return a list of Examples using the given environment.""" if environment is None: environment = get_env() with environment: x = Symbol("x", BOOL) y = Symbol("y", BOOL) p = Symbol("p", INT) q = Symbol("q", INT) r = Symbol("r", REAL) s = Symbol("s", REAL) aii = Symbol("aii", ARRAY_INT_INT) ari = Symbol("ari", ArrayType(REAL, INT)) arb = Symbol("arb", ArrayType(REAL, BV8)) abb = Symbol("abb", ArrayType(BV8, BV8)) nested_a = Symbol("a_arb_aii", ArrayType(ArrayType(REAL, BV8), ARRAY_INT_INT)) rf = Symbol("rf", FunctionType(REAL, [REAL, REAL])) rg = Symbol("rg", FunctionType(REAL, [REAL])) ih = Symbol("ih", FunctionType(INT, [REAL, INT])) ig = Symbol("ig", FunctionType(INT, [INT])) bf = Symbol("bf", FunctionType(BOOL, [BOOL])) bg = Symbol("bg", FunctionType(BOOL, [BOOL])) bv8 = Symbol("bv1", BV8) bv16 = Symbol("bv2", BV16) result = [ # Formula, is_valid, is_sat, is_qf Example(hr="(x & y)", expr=And(x, y), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BOOL), Example(hr="(x <-> y)", expr=Iff(x, y), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BOOL), Example(hr="((x | y) & (! (x | y)))", expr=And(Or(x, y), Not(Or(x, y))), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BOOL), Example(hr="(x & (! y))", expr=And(x, Not(y)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BOOL), Example(hr="(False -> True)", expr=Implies(FALSE(), TRUE()), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BOOL), # # LIA # Example(hr="((q < p) & (x -> y))", expr=And(GT(p, q), Implies(x, y)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_IDL), Example(hr="(((p + q) = 5) & (q < p))", expr=And(Equals(Plus(p, q), Int(5)), GT(p, q)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LIA), Example(hr="((q <= p) | (p <= q))", expr=Or(GE(p, q), LE(p, q)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_IDL), Example(hr="(! (p < (q * 2)))", expr=Not(LT(p, Times(q, Int(2)))), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LIA), Example(hr="(p < (p - (5 - 2)))", expr=GT(Minus(p, Minus(Int(5), Int(2))), p), is_valid=False, is_sat=False, logic=pysmt.logics.QF_IDL), Example(hr="((x ? 7 : ((p + -1) * 3)) = q)", expr=Equals( Ite(x, Int(7), Times(Plus(p, Int(-1)), Int(3))), q), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LIA), Example(hr="(p < (q + 1))", expr=LT(p, Plus(q, Int(1))), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LIA), # # LRA # Example(hr="((s < r) & (x -> y))", expr=And(GT(r, s), Implies(x, y)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_RDL), Example(hr="(((r + s) = 28/5) & (s < r))", expr=And(Equals(Plus(r, s), Real(Fraction("5.6"))), GT(r, s)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LRA), Example(hr="((s <= r) | (r <= s))", expr=Or(GE(r, s), LE(r, s)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_RDL), Example(hr="(! ((r * 2.0) < (s * 2.0)))", expr=Not(LT(Div(r, Real((1, 2))), Times(s, Real(2)))), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LRA), Example(hr="(! (r < (r - (5.0 - 2.0))))", expr=Not(GT(Minus(r, Minus(Real(5), Real(2))), r)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_RDL), Example(hr="((x ? 7.0 : ((s + -1.0) * 3.0)) = r)", expr=Equals( Ite(x, Real(7), Times(Plus(s, Real(-1)), Real(3))), r), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LRA), # # EUF # Example(hr="(bf(x) <-> bg(x))", expr=Iff(Function(bf, (x, )), Function(bg, (x, ))), is_valid=False, is_sat=True, logic=pysmt.logics.QF_UF), Example(hr="(rf(5.0, rg(r)) = 0.0)", expr=Equals(Function(rf, (Real(5), Function(rg, (r, )))), Real(0)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_UFLRA), Example(hr="((rg(r) = (5.0 + 2.0)) <-> (rg(r) = 7.0))", expr=Iff(Equals(Function(rg, [r]), Plus(Real(5), Real(2))), Equals(Function(rg, [r]), Real(7))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_UFLRA), Example( hr="((r = (s + 1.0)) & (rg(s) = 5.0) & (rg((r - 1.0)) = 7.0))", expr=And([ Equals(r, Plus(s, Real(1))), Equals(Function(rg, [s]), Real(5)), Equals(Function(rg, [Minus(r, Real(1))]), Real(7)) ]), is_valid=False, is_sat=False, logic=pysmt.logics.QF_UFLRA), # # BV # Example(hr="((1_32 & 0_32) = 0_32)", expr=Equals(BVAnd(BVOne(32), BVZero(32)), BVZero(32)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((! 2_3) = 5_3)", expr=Equals(BVNot(BV("010")), BV("101")), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((7_3 xor 0_3) = 0_3)", expr=Equals(BVXor(BV("111"), BV("000")), BV("000")), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="((bv1::bv1) u< 0_16)", expr=BVULT(BVConcat(bv8, bv8), BVZero(16)), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="(1_32[0:7] = 1_8)", expr=Equals(BVExtract(BVOne(32), end=7), BVOne(8)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="(0_8 u< (((bv1 + 1_8) * 5_8) u/ 5_8))", expr=BVUGT( BVUDiv(BVMul(BVAdd(bv8, BVOne(8)), BV(5, width=8)), BV(5, width=8)), BVZero(8)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="(0_16 u<= bv2)", expr=BVUGE(bv16, BVZero(16)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="(0_16 s<= bv2)", expr=BVSGE(bv16, BVZero(16)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BV), Example( hr="((0_32 u< (5_32 u% 2_32)) & ((5_32 u% 2_32) u<= 1_32))", expr=And( BVUGT(BVURem(BV(5, width=32), BV(2, width=32)), BVZero(32)), BVULE(BVURem(BV(5, width=32), BV(2, width=32)), BVOne(32))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((((1_32 + (- 1_32)) << 1_32) >> 1_32) = 1_32)", expr=Equals( BVLShr(BVLShl(BVAdd(BVOne(32), BVNeg(BVOne(32))), 1), 1), BVOne(32)), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="((1_32 - 1_32) = 0_32)", expr=Equals(BVSub(BVOne(32), BVOne(32)), BVZero(32)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), # Rotations Example(hr="(((1_32 ROL 1) ROR 1) = 1_32)", expr=Equals(BVRor(BVRol(BVOne(32), 1), 1), BVOne(32)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), # Extensions Example(hr="((0_5 ZEXT 11) = (0_1 SEXT 15))", expr=Equals(BVZExt(BVZero(5), 11), BVSExt(BVZero(1), 15)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 - bv2) = 0_16)", expr=Equals(BVSub(bv16, bv16), BVZero(16)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 - bv2)[0:7] = bv1)", expr=Equals(BVExtract(BVSub(bv16, bv16), 0, 7), bv8), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2[0:7] bvcomp bv1) = 1_1)", expr=Equals(BVComp(BVExtract(bv16, 0, 7), bv8), BVOne(1)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 bvcomp bv2) = 0_1)", expr=Equals(BVComp(bv16, bv16), BVZero(1)), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="(bv2 s< bv2)", expr=BVSLT(bv16, bv16), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="(bv2 s< 0_16)", expr=BVSLT(bv16, BVZero(16)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 s< 0_16) | (0_16 s<= bv2))", expr=Or(BVSGT(BVZero(16), bv16), BVSGE(bv16, BVZero(16))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="(bv2 u< bv2)", expr=BVULT(bv16, bv16), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="(bv2 u< 0_16)", expr=BVULT(bv16, BVZero(16)), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="((bv2 | 0_16) = bv2)", expr=Equals(BVOr(bv16, BVZero(16)), bv16), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 & 0_16) = 0_16)", expr=Equals(BVAnd(bv16, BVZero(16)), BVZero(16)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((0_16 s< bv2) & ((bv2 s/ 65535_16) s< 0_16))", expr=And(BVSLT(BVZero(16), bv16), BVSLT(BVSDiv(bv16, SBV(-1, 16)), BVZero(16))), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((0_16 s< bv2) & ((bv2 s% 1_16) s< 0_16))", expr=And(BVSLT(BVZero(16), bv16), BVSLT(BVSRem(bv16, BVOne(16)), BVZero(16))), is_valid=False, is_sat=False, logic=pysmt.logics.QF_BV), Example(hr="((bv2 u% 1_16) = 0_16)", expr=Equals(BVURem(bv16, BVOne(16)), BVZero(16)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 s% 1_16) = 0_16)", expr=Equals(BVSRem(bv16, BVOne(16)), BVZero(16)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 s% (- 1_16)) = 0_16)", expr=Equals(BVSRem(bv16, BVNeg(BVOne(16))), BVZero(16)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((bv2 a>> 0_16) = bv2)", expr=Equals(BVAShr(bv16, BVZero(16)), bv16), is_valid=True, is_sat=True, logic=pysmt.logics.QF_BV), Example(hr="((0_16 s<= bv2) & ((bv2 a>> 1_16) = (bv2 >> 1_16)))", expr=And( BVSLE(BVZero(16), bv16), Equals(BVAShr(bv16, BVOne(16)), BVLShr(bv16, BVOne(16)))), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BV), # # Quantification # Example(hr="(forall y . (x -> y))", expr=ForAll([y], Implies(x, y)), is_valid=False, is_sat=True, logic=pysmt.logics.BOOL), Example(hr="(forall p, q . ((p + q) = 0))", expr=ForAll([p, q], Equals(Plus(p, q), Int(0))), is_valid=False, is_sat=False, logic=pysmt.logics.LIA), Example( hr="(forall r, s . (((0.0 < r) & (0.0 < s)) -> ((r - s) < r)))", expr=ForAll([r, s], Implies(And(GT(r, Real(0)), GT(s, Real(0))), (LT(Minus(r, s), r)))), is_valid=True, is_sat=True, logic=pysmt.logics.LRA), Example(hr="(exists x, y . (x -> y))", expr=Exists([x, y], Implies(x, y)), is_valid=True, is_sat=True, logic=pysmt.logics.BOOL), Example(hr="(exists p, q . ((p + q) = 0))", expr=Exists([p, q], Equals(Plus(p, q), Int(0))), is_valid=True, is_sat=True, logic=pysmt.logics.LIA), Example(hr="(exists r . (forall s . (r < (r - s))))", expr=Exists([r], ForAll([s], GT(Minus(r, s), r))), is_valid=False, is_sat=False, logic=pysmt.logics.LRA), Example(hr="(forall r . (exists s . (r < (r - s))))", expr=ForAll([r], Exists([s], GT(Minus(r, s), r))), is_valid=True, is_sat=True, logic=pysmt.logics.LRA), Example(hr="(x & (forall r . ((r + s) = 5.0)))", expr=And(x, ForAll([r], Equals(Plus(r, s), Real(5)))), is_valid=False, is_sat=False, logic=pysmt.logics.LRA), Example(hr="(exists x . ((x <-> (5.0 < s)) & (s < 3.0)))", expr=Exists([x], (And(Iff(x, GT(s, Real(5))), LT(s, Real(3))))), is_valid=False, is_sat=True, logic=pysmt.logics.LRA), # # UFLIRA # Example(hr="((p < ih(r, q)) & (x -> y))", expr=And(GT(Function(ih, (r, q)), p), Implies(x, y)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_UFLIRA), Example( hr= "(((p - 3) = q) -> ((p < ih(r, (q + 3))) | (ih(r, p) <= p)))", expr=Implies( Equals(Minus(p, Int(3)), q), Or(GT(Function(ih, (r, Plus(q, Int(3)))), p), LE(Function(ih, (r, p)), p))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_UFLIRA), Example( hr= "(((ToReal((p - 3)) = r) & (ToReal(q) = r)) -> ((p < ih(ToReal((p - 3)), (q + 3))) | (ih(r, p) <= p)))", expr=Implies( And(Equals(ToReal(Minus(p, Int(3))), r), Equals(ToReal(q), r)), Or( GT( Function( ih, (ToReal(Minus(p, Int(3))), Plus(q, Int(3)))), p), LE(Function(ih, (r, p)), p))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_UFLIRA), Example( hr= "(! (((ToReal((p - 3)) = r) & (ToReal(q) = r)) -> ((p < ih(ToReal((p - 3)), (q + 3))) | (ih(r, p) <= p))))", expr=Not( Implies( And(Equals(ToReal(Minus(p, Int(3))), r), Equals(ToReal(q), r)), Or( GT( Function(ih, (ToReal(Minus( p, Int(3))), Plus(q, Int(3)))), p), LE(Function(ih, (r, p)), p)))), is_valid=False, is_sat=False, logic=pysmt.logics.QF_UFLIRA), Example( hr= """("Did you know that any string works? #yolo" & "10" & "|#somesolverskeepthe||" & " ")""", expr=And(Symbol("Did you know that any string works? #yolo"), Symbol("10"), Symbol("|#somesolverskeepthe||"), Symbol(" ")), is_valid=False, is_sat=True, logic=pysmt.logics.QF_BOOL), # # Arrays # Example(hr="((q = 0) -> (aii[0 := 0] = aii[0 := q]))", expr=Implies( Equals(q, Int(0)), Equals(Store(aii, Int(0), Int(0)), Store(aii, Int(0), q))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_ALIA), Example(hr="(aii[0 := 0][0] = 0)", expr=Equals(Select(Store(aii, Int(0), Int(0)), Int(0)), Int(0)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_ALIA), Example(hr="((Array{Int, Int}(0)[1 := 1] = aii) & (aii[1] = 0))", expr=And(Equals(Array(INT, Int(0), {Int(1): Int(1)}), aii), Equals(Select(aii, Int(1)), Int(0))), is_valid=False, is_sat=False, logic=pysmt.logics.get_logic_by_name("QF_ALIA*")), Example(hr="((Array{Int, Int}(0)[1 := 3] = aii) & (aii[1] = 3))", expr=And(Equals(Array(INT, Int(0), {Int(1): Int(3)}), aii), Equals(Select(aii, Int(1)), Int(3))), is_valid=False, is_sat=True, logic=pysmt.logics.get_logic_by_name("QF_ALIA*")), Example(hr="((Array{Real, Int}(10) = ari) & (ari[6/5] = 0))", expr=And(Equals(Array(REAL, Int(10)), ari), Equals(Select(ari, Real((6, 5))), Int(0))), is_valid=False, is_sat=False, logic=pysmt.logics.get_logic_by_name("QF_AUFBVLIRA*")), Example( hr= "((Array{Real, Int}(0)[1.0 := 10][2.0 := 20][3.0 := 30][4.0 := 40] = ari) & (! ((ari[0.0] = 0) & (ari[1.0] = 10) & (ari[2.0] = 20) & (ari[3.0] = 30) & (ari[4.0] = 40))))", expr=And( Equals( Array( REAL, Int(0), { Real(1): Int(10), Real(2): Int(20), Real(3): Int(30), Real(4): Int(40) }), ari), Not( And(Equals(Select(ari, Real(0)), Int(0)), Equals(Select(ari, Real(1)), Int(10)), Equals(Select(ari, Real(2)), Int(20)), Equals(Select(ari, Real(3)), Int(30)), Equals(Select(ari, Real(4)), Int(40))))), is_valid=False, is_sat=False, logic=pysmt.logics.get_logic_by_name("QF_AUFBVLIRA*")), Example( hr= "((Array{Real, Int}(0)[1.0 := 10][2.0 := 20][3.0 := 30][4.0 := 40][5.0 := 50] = ari) & (! ((ari[0.0] = 0) & (ari[1.0] = 10) & (ari[2.0] = 20) & (ari[3.0] = 30) & (ari[4.0] = 40) & (ari[5.0] = 50))))", expr=And( Equals( Array( REAL, Int(0), { Real(1): Int(10), Real(2): Int(20), Real(3): Int(30), Real(4): Int(40), Real(5): Int(50) }), ari), Not( And(Equals(Select(ari, Real(0)), Int(0)), Equals(Select(ari, Real(1)), Int(10)), Equals(Select(ari, Real(2)), Int(20)), Equals(Select(ari, Real(3)), Int(30)), Equals(Select(ari, Real(4)), Int(40)), Equals(Select(ari, Real(5)), Int(50))))), is_valid=False, is_sat=False, logic=pysmt.logics.get_logic_by_name("QF_AUFBVLIRA*")), Example( hr= "((a_arb_aii = Array{Array{Real, BV{8}}, Array{Int, Int}}(Array{Int, Int}(7))) -> (a_arb_aii[arb][42] = 7))", expr=Implies( Equals(nested_a, Array(ArrayType(REAL, BV8), Array(INT, Int(7)))), Equals(Select(Select(nested_a, arb), Int(42)), Int(7))), is_valid=True, is_sat=True, logic=pysmt.logics.get_logic_by_name("QF_AUFBVLIRA*")), Example(hr="(abb[bv1 := y_][bv1 := z_] = abb[bv1 := z_])", expr=Equals( Store(Store(abb, bv8, Symbol("y_", BV8)), bv8, Symbol("z_", BV8)), Store(abb, bv8, Symbol("z_", BV8))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_ABV), Example(hr="((r / s) = (r * s))", expr=Equals(Div(r, s), Times(r, s)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_NRA), Example(hr="(2.0 = (r * r))", expr=Equals(Real(2), Times(r, r)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_NRA), Example(hr="((p ^ 2) = 0)", expr=Equals(Pow(p, Int(2)), Int(0)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_NIA), Example(hr="((r ^ 2.0) = 0.0)", expr=Equals(Pow(r, Real(2)), Real(0)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_NRA), Example(hr="((r * r * r) = 25.0)", expr=Equals(Times(r, r, r), Real(25)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_NRA), Example(hr="((5.0 * r * 5.0) = 25.0)", expr=Equals(Times(Real(5), r, Real(5)), Real(25)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LRA), Example(hr="((p * p * p) = 25)", expr=Equals(Times(p, p, p), Int(25)), is_valid=False, is_sat=False, logic=pysmt.logics.QF_NIA), Example(hr="((5 * p * 5) = 25)", expr=Equals(Times(Int(5), p, Int(5)), Int(25)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LIA), Example(hr="(((1 - 1) * p * 1) = 0)", expr=Equals(Times(Minus(Int(1), Int(1)), p, Int(1)), Int(0)), is_valid=True, is_sat=True, logic=pysmt.logics.QF_LIA), # Huge Fractions: Example( hr= "((r * 1606938044258990275541962092341162602522202993782792835301376/7) = -20480000000000000000000000.0)", expr=Equals(Times(r, Real(Fraction(2**200, 7))), Real(-200**11)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_LRA), Example(hr="(((r + 5.0 + s) * (s + 2.0 + r)) = 0.0)", expr=Equals( Times(Plus(r, Real(5), s), Plus(s, Real(2), r)), Real(0)), is_valid=False, is_sat=True, logic=pysmt.logics.QF_NRA), Example( hr= "(((p + 5 + q) * (p - (q - 5))) = ((p * p) + (10 * p) + 25 + (-1 * q * q)))", expr=Equals( Times(Plus(p, Int(5), q), Minus(p, Minus(q, Int(5)))), Plus(Times(p, p), Times(Int(10), p), Int(25), Times(Int(-1), q, q))), is_valid=True, is_sat=True, logic=pysmt.logics.QF_NIA), ] return result
def test_array_value_get(self): ax = Array( REAL, Real(0), { Real(1): Real(2), Real(2): Real(3), Real(3): Real(4), Real(4): Real(5), }) self.assertEqual(ax.array_value_get(Real(1)), Real(2)) self.assertEqual(ax.array_value_get(Real(2)), Real(3)) self.assertEqual(ax.array_value_get(Real(3)), Real(4)) self.assertEqual(ax.array_value_get(Real(4)), Real(5)) self.assertEqual(ax.array_value_get(Real(-1)), Real(0)) self.assertEqual(ax.array_value_get(Real(5)), Real(0))
def test_times_distributivity(self): r = Symbol("r", REAL) s = Symbol("s", REAL) td = TimesDistributor() f = Times(Plus(r, Real(1)), Real(3)) fp = td.walk(f) self.assertValid(Equals(f, fp), (f, fp)) f = Times(Plus(r, Real(1)), s) fp = td.walk(f) self.assertValid(Equals(f, fp), (f, fp)) f = Times(Plus(r, Real(1), s), Real(3)) fp = td.walk(f) self.assertValid(Equals(f, fp), (f, fp)) f = Times(Minus(r, Real(1)), Real(3)) fp = td.walk(f) self.assertValid(Equals(f, fp), (f, fp)) f = Times(Minus(r, Real(1)), s) fp = td.walk(f) self.assertValid(Equals(f, fp), (f, fp)) f = Times(Minus(Real(1), s), Real(3)) fp = td.walk(f) self.assertValid(Equals(f, fp), (f, fp)) f = Times(Minus(r, Real(1)), Plus(r, s)) fp = td.walk(f) self.assertValid(Equals(f, fp), (f, fp)) # (r + 1) * (s-1) = r*s + (-r) + s - 1 f = Times(Plus(r, Real(1)), Minus(s, Real(1))) fp = td.walk(f).simplify() target = Plus(Times(r, s), Times(r, Real(-1)), s, Real(-1)) self.assertValid(Equals(fp, target), fp) self.assertTrue(fp.is_plus(), fp)
def test_normalization(): def get_normalization_file(filename): return path.join(path.dirname(__file__), "res", "renorm_bug", filename) for i in range(5): domain = Domain.from_file(get_normalization_file("domain.json")) support = read_smtlib(get_normalization_file("vanilla.support")) weight = read_smtlib(get_normalization_file("vanilla.weight")) new_support = read_smtlib( get_normalization_file("renorm_chi_{}.support".format(i))) # print(smt_to_nested(support)) clean_support = normalize_formula(support) clean_new_support = normalize_formula(new_support) clean_weight = normalize_formula(weight) print(smt_to_nested(clean_weight)) assert (RejectionEngine(domain, ~Iff(support, clean_support), Real(1.0), 1000000).compute_volume() == 0) assert (RejectionEngine(domain, ~Iff(new_support, clean_new_support), Real(1.0), 1000000).compute_volume() == 0) # plot_formula("new_support", domain, new_support, ["r0", "r1"]) # plot_formula("clean_new_support", domain, clean_new_support, ["r0", "r1"]) support = clean_support new_support = clean_new_support weight = clean_weight # print(RejectionEngine(domain, Iff(weight, ~clean_weight), Real(1.0), 1000000).compute_volume()) # print(smt_to_nested(support)) print("Problem", i) engine = XaddEngine(domain, support, weight, "original") print("Volume before starting", engine.compute_volume()) new_weight = engine.normalize(new_support, paths=False) Density(domain, new_support, new_weight).to_file("normalized.json") illegal_volume = XaddEngine(domain, ~new_support, new_weight, "mass").compute_volume() assert illegal_volume == pytest.approx(0, rel=EXACT_REL_ERROR) computed_volume = XaddEngine(domain, TRUE(), new_weight, "mass").compute_volume() computed_volume_within = XaddEngine(domain, new_support, new_weight, "mass").compute_volume() computed_volume_within2 = XaddEngine(domain, new_support, new_weight).compute_volume() computed_volume_within3 = RejectionEngine(domain, new_support, new_weight, 1000000).compute_volume() print( "pa new_support new_weight", computed_volume_within, "xadd new_support new_weight:", computed_volume_within2, "rej new_support new_weight:", computed_volume_within3, ) assert computed_volume_within == pytest.approx(computed_volume_within2, EXACT_REL_ERROR) print( "pa true new_weight:", computed_volume, "pa new_support new_weight", computed_volume_within, "pa outside new_support new_weight", illegal_volume, ) assert computed_volume == pytest.approx(1, rel=EXACT_REL_ERROR) assert computed_volume_within == pytest.approx(1, rel=EXACT_REL_ERROR) illegal_volume = engine.copy_with(support=~new_support, weight=new_weight).compute_volume() assert illegal_volume == pytest.approx(0, rel=EXACT_REL_ERROR)
def test_sample(self, sample): """ Check whether or not the encoding "predicts" the same class as the classifier given an input sample. """ # first, compute the scores for all classes as would be # predicted by the classifier # score arrays computed for each class csum = [[] for c in range(self.nofcl)] if self.optns.verb: print('testing sample:', list(sample)) sample_internal = list(self.xgb.transform(sample)[0]) # traversing all trees for i, tree in enumerate(self.ensemble.trees): # getting class id clid = i % self.nofcl # a score computed by the current tree score = scores_tree(tree, sample_internal) # this tree contributes to class with clid csum[clid].append(score) # final scores for each class cscores = [sum(scores) for scores in csum] # second, get the scores computed with the use of the encoding # asserting the sample hypos = [] if not self.intvs: for i, fval in enumerate(sample_internal): feat, vid = self.xgb.transform_inverse_by_index(i) fid = self.feats[feat] if vid == None: fvar = Symbol('f{0}'.format(fid), typename=REAL) hypos.append(Equals(fvar, Real(float(fval)))) else: fvar = Symbol('f{0}_{1}'.format(fid, vid), typename=BOOL) if int(fval) == 1: hypos.append(fvar) else: hypos.append(Not(fvar)) else: for i, fval in enumerate(sample_internal): feat, _ = self.xgb.transform_inverse_by_index(i) feat = 'f{0}'.format(self.feats[feat]) # determining the right interval and the corresponding variable for ub, fvar in zip(self.intvs[feat], self.ivars[feat]): if ub == '+' or fval < ub: hypos.append(fvar) break else: assert 0, 'No proper interval found for {0}'.format(feat) # now, getting the model escores = [] model = get_model(And(self.enc, *hypos), solver_name=self.optns.solver) for c in range(self.nofcl): v = Symbol('class{0}_score'.format(c), typename=REAL) escores.append(float(model.get_py_value(v))) assert all(map(lambda c, e: abs(c - e) <= 0.001, cscores, escores)), \ 'wrong prediction: {0} vs {1}'.format(cscores, escores) if self.optns.verb: print('xgb scores:', cscores) print('enc scores:', escores)
def expr_to_pysmt(context: TranslationContext, expr: Expr, *, is_expectation: bool = False, allow_infinity: bool = False) -> FNode: """ Translate a pGCL expression to a pySMT formula. Note that substitution expressions are not allowed here (they are not supported in pySMT). You can pass in the optional `is_expectation` parameter to have all integer values converted to real values. If `allow_infinity` is `True`, then infinity expressions will be mapped directly to the `infinity` variable of the given :py:class:`TranslationContext`. Take care to appropriately constrain the `infinity` variable! Note that arithmetic expressions may not contain infinity, to prevent expressions like `infinity - infinity`. .. doctest:: >>> from probably.pgcl.parser import parse_expr >>> from pysmt.shortcuts import Symbol >>> from pysmt.typing import INT >>> expr = parse_expr("x + 4 * 13") >>> context = TranslationContext({"x": Symbol("x", INT)}) >>> expr_to_pysmt(context, expr) (x + (4 * 13)) """ if isinstance(expr, BoolLitExpr): return TRUE() if expr.value else FALSE() elif isinstance(expr, NatLitExpr): if is_expectation: return ToReal(Int(expr.value)) else: return Int(expr.value) elif isinstance(expr, FloatLitExpr): if expr.is_infinite(): if not allow_infinity: raise Exception( f"Infinity is not allowed in this expression: {expr}") return context.infinity else: return Real(Fraction(expr.value)) elif isinstance(expr, VarExpr): var = context.variables[expr.var] if is_expectation and get_type(var) == INT: var = ToReal(var) return var elif isinstance(expr, UnopExpr): operand = expr_to_pysmt(context, expr.expr, is_expectation=False, allow_infinity=allow_infinity) if expr.operator == Unop.NEG: return Not(operand) elif expr.operator == Unop.IVERSON: return Ite(operand, Real(1), Real(0)) elif isinstance(expr, BinopExpr): # `is_expectation` is disabled if we enter a non-arithmetic expression # (we do not convert integers to reals within a boolean expression such # as `x == y`, for example). # # Similarly, `allow_infinity` is disabled if we enter an arithmetic # expression because calculations with infinity are hard to make sense of. is_arith_op = expr.operator in [Binop.PLUS, Binop.MINUS, Binop.TIMES] is_expectation = is_expectation # TODO: and is_arith_op allow_infinity = allow_infinity # TODO: and not is_arith_op?!??! lhs = expr_to_pysmt(context, expr.lhs, is_expectation=is_expectation, allow_infinity=allow_infinity) rhs = expr_to_pysmt(context, expr.rhs, is_expectation=is_expectation, allow_infinity=allow_infinity) if expr.operator == Binop.OR: return Or(lhs, rhs) elif expr.operator == Binop.AND: return And(lhs, rhs) elif expr.operator == Binop.LEQ: return LE(lhs, rhs) elif expr.operator == Binop.LE: return LT(lhs, rhs) elif expr.operator == Binop.EQ: return EqualsOrIff(lhs, rhs) elif expr.operator == Binop.PLUS: return Plus(lhs, rhs) elif expr.operator == Binop.MINUS: return Ite(LE(lhs, rhs), (Int(0) if get_type(lhs) == INT else Real(0)), Minus(lhs, rhs)) elif expr.operator == Binop.TIMES: return Times(lhs, rhs) elif isinstance(expr, SubstExpr): raise Exception("Substitution expression is not allowed here.") raise Exception("unreachable")
def prepare(self, sample, expl): """ Prepare the oracle for validating an explanation given a sample. """ if self.selv: # disable the previous assumption if any self.oracle.add_assertion(Not(self.selv)) # creating a fresh selector for a new sample sname = ','.join([str(v).strip() for v in sample]) # the samples should not repeat; otherwise, they will be # inconsistent with the previously introduced selectors assert sname not in self.idmgr.obj2id, 'this sample has been considered before (sample {0})'.format( self.idmgr.id(sname)) self.selv = Symbol('sample{0}_selv'.format(self.idmgr.id(sname)), typename=BOOL) self.rhypos = [] # relaxed hypotheses # transformed sample self.sample = list(self.xgb.transform(sample)[0]) # preparing the selectors for i, (inp, val) in enumerate(zip(self.inps, self.sample), 1): feat = inp.symbol_name().split('_')[0] selv = Symbol('selv_{0}'.format(feat)) val = float(val) self.rhypos.append(selv) # adding relaxed hypotheses to the oracle for inp, val, sel in zip(self.inps, self.sample, self.rhypos): if '_' not in inp.symbol_name(): hypo = Implies(self.selv, Implies(sel, Equals(inp, Real(float(val))))) else: hypo = Implies(self.selv, Implies(sel, inp if val else Not(inp))) self.oracle.add_assertion(hypo) # propagating the true observation if self.oracle.solve([self.selv] + self.rhypos): model = self.oracle.get_model() else: assert 0, 'Formula is unsatisfiable under given assumptions' # choosing the maximum outvals = [float(model.get_py_value(o)) for o in self.outs] maxoval = max(zip(outvals, range(len(outvals)))) # correct class id (corresponds to the maximum computed) true_output = maxoval[1] # forcing a misclassification, i.e. a wrong observation disj = [] for i in range(len(self.outs)): if i != true_output: disj.append(GT(self.outs[i], self.outs[true_output])) self.oracle.add_assertion(Implies(self.selv, Or(disj))) # removing all hypotheses except for those in the explanation hypos = [] for i, hypo in enumerate(self.rhypos): j = self.ftids[self.xgb.transform_inverse_by_index(i)[0]] if j in expl: hypos.append(hypo) self.rhypos = hypos if self.verbose: inpvals = self.xgb.readable_sample(sample) preamble = [] for f, v in zip(self.xgb.feature_names, inpvals): if f not in v: preamble.append('{0} = {1}'.format(f, v)) else: preamble.append(v) print(' explanation for: "IF {0} THEN {1}"'.format( ' AND '.join(preamble), self.xgb.target_name[true_output]))
def minReward(intents, queries, tuples, matches, minRewardValue): userStrategy = dict() dbmsStrategy = dict() rewardMatrix = constructRewardMatrix(intents, tuples, matches) letters = set() #User Strategy matrix, each cell holds a variable for the SMT for intent in intents: if intent not in userStrategy: userStrategy[intent] = dict() for query in queries: userStrategy[intent][query] = Symbol('User['+intent + '][' + query + ']', REAL) letters.add(userStrategy[intent][query]) #DBMS Strategy matrix, each cell holds a variable for the SMT for query in queries: if query not in dbmsStrategy: dbmsStrategy[query] = dict() for tup in tuples: dbmsStrategy[query][tup] = Symbol('DBMS['+query + '][' + tup + ']', REAL) letters.add(dbmsStrategy[query][tup]) #Tells the range of values, for now set to Pure strategy so [0,1] domains = And([Or(Equals(l, Real(0)), Equals(l, Real(1))) for l in letters]) #Adds each row of the user strategy stochasticMatrixUser = list() for intent in intents: stochasticMatrixUser.append(Plus(userStrategy[intent].values())) #Adds each row of the DBMS strategy stochasticMatrixdbms = list() for query in queries: stochasticMatrixdbms.append(Plus(dbmsStrategy[query].values())) #Checks the rows to make sure that the strategies are row stochastic stochUserEquals = [Equals(x, Real(1)) for x in stochasticMatrixUser] stochdbmsEquals = [Equals(x, Real(1)) for x in stochasticMatrixdbms] allEquals = stochdbmsEquals + stochUserEquals #Stochastic problem stochasticProblem = And(allEquals) if DEBUG: print('\nStochastic Serialization: ') print(stochasticProblem) #Uses Formula 1 from my paper to calculate the payoff reward = list() for intent in intents: for query in queries: for tup in tuples: reward.append(Times(Real(1/len(intents)), userStrategy[intent][query], dbmsStrategy[query][tup], rewardMatrix[intent][tup])) #Reward problem, requires a minimum reward. May not always be possible to achieve this reward rewardProblem = GE(Plus(reward), Real(minRewardValue)) if DEBUG: print('\nReward Serialization: ') print(rewardProblem) with Solver(name="z3") as solver: solver.add_assertion(domains) if not solver.solve(): print('No solultion available') return print('Can satisfy domains') solver.add_assertion(stochasticProblem) if not solver.solve(): print('No sulution available') return print('Can satisfy stochastic') solver.add_assertion(rewardProblem) if not solver.solve(): print('No solution available') return print('Can satisfy minimum reward') for intent in intents: for query in queries: print("%s = %s" % (userStrategy[intent][query], solver.get_value(userStrategy[intent][query]))) for query in queries: for tup in tuples: print("%s = %s" % (dbmsStrategy[query][tup], solver.get_value(dbmsStrategy[query][tup])))
else: raise WMIParsingError("Unhandled formula format", formula) if __name__ == "__main__": from pysmt.shortcuts import Symbol, Ite, And, LE, LT, Real, Times, serialize from pysmt.typing import REAL def compute_print(method, query, evidence): print("query: ", serialize(query)) print("evidence: ", serialize(evidence) if evidence else "-") prob = method.compute_normalized_probability(query, evidence) print("normalized: ", prob) print("--------------------------------------------------") x = Symbol("x", REAL) A = Symbol("A") support = And(LE(Real(-1), x), LE(x, Real(1))) weights = Ite(LT(Real(0), x), Ite(A, Times(Real(2), x), x), Ite(A, Times(Real(-2), x), Times(Real(-1), x))) praise = PRAiSEInference(support, weights) print("support: ", serialize(support)) print("weights: ", serialize(weights)) print("==================================================") suite = [(A, None), (And(A, LE(Real(0), x)), None), (LE(Real(0), x), A)] for query, evidence in suite: compute_print(praise, query, evidence)
def prepare(self, sample): """ Prepare the oracle for computing an explanation. """ if self.selv: # disable the previous assumption if any self.oracle.add_assertion(Not(self.selv)) # creating a fresh selector for a new sample sname = ','.join([str(v).strip() for v in sample]) # the samples should not repeat; otherwise, they will be # inconsistent with the previously introduced selectors assert sname not in self.idmgr.obj2id, 'this sample has been considered before (sample {0})'.format( self.idmgr.id(sname)) self.selv = Symbol('sample{0}_selv'.format(self.idmgr.id(sname)), typename=BOOL) self.rhypos = [] # relaxed hypotheses # transformed sample self.sample = list(self.xgb.transform(sample)[0]) self.sel2fid = {} # selectors to original feature ids self.sel2vid = {} # selectors to categorical feature ids # preparing the selectors for i, (inp, val) in enumerate(zip(self.inps, self.sample), 1): feat = inp.symbol_name().split('_')[0] selv = Symbol('selv_{0}'.format(feat)) val = float(val) self.rhypos.append(selv) if selv not in self.sel2fid: self.sel2fid[selv] = int(feat[1:]) self.sel2vid[selv] = [i - 1] else: self.sel2vid[selv].append(i - 1) # adding relaxed hypotheses to the oracle if not self.intvs: for inp, val, sel in zip(self.inps, self.sample, self.rhypos): if '_' not in inp.symbol_name(): hypo = Implies(self.selv, Implies(sel, Equals(inp, Real(float(val))))) else: hypo = Implies(self.selv, Implies(sel, inp if val else Not(inp))) self.oracle.add_assertion(hypo) else: for inp, val, sel in zip(self.inps, self.sample, self.rhypos): inp = inp.symbol_name() # determining the right interval and the corresponding variable for ub, fvar in zip(self.intvs[inp], self.ivars[inp]): if ub == '+' or val < ub: hypo = Implies(self.selv, Implies(sel, fvar)) break self.oracle.add_assertion(hypo) # in case of categorical data, there are selector duplicates # and we need to remove them self.rhypos = sorted(set(self.rhypos), key=lambda x: int(x.symbol_name()[6:])) # propagating the true observation if self.oracle.solve([self.selv] + self.rhypos): model = self.oracle.get_model() else: assert 0, 'Formula is unsatisfiable under given assumptions' # choosing the maximum outvals = [float(model.get_py_value(o)) for o in self.outs] maxoval = max(zip(outvals, range(len(outvals)))) # correct class id (corresponds to the maximum computed) self.out_id = maxoval[1] self.output = self.xgb.target_name[self.out_id] # forcing a misclassification, i.e. a wrong observation disj = [] for i in range(len(self.outs)): if i != self.out_id: disj.append(GT(self.outs[i], self.outs[self.out_id])) self.oracle.add_assertion(Implies(self.selv, Or(disj))) if self.verbose: inpvals = self.xgb.readable_sample(sample) self.preamble = [] for f, v in zip(self.xgb.feature_names, inpvals): if f not in v: self.preamble.append('{0} = {1}'.format(f, v)) else: self.preamble.append(v) print(' explaining: "IF {0} THEN {1}"'.format( ' AND '.join(self.preamble), self.output))
def findNashEquilibria(intents, queries, tuples, matches, strict, minReward, minRewardValue): userStrategy = dict() dbmsStrategy = dict() nashUserStrategy = dict() nashdbmsStrategy = dict() rewardMatrix = constructRewardMatrix(intents, tuples, matches) letters = set() nashRestrictions = list() #User Strategy matrix, each cell holds a variable for the SMT for intent in intents: if intent not in userStrategy: userStrategy[intent] = dict() for query in queries: userStrategy[intent][query] = Symbol('User['+intent + '][' + query + ']', REAL) letters.add(userStrategy[intent][query]) #DBMS Strategy matrix, each cell holds a variable for the SMT for query in queries: if query not in dbmsStrategy: dbmsStrategy[query] = dict() for tup in tuples: dbmsStrategy[query][tup] = Symbol('DBMS['+query + '][' + tup + ']', REAL) letters.add(dbmsStrategy[query][tup]) #We create a strategy for each move that can be made from the current position #This means that only a single row changes, the rest of the rows are the same as the strategies above #There also needs to be a strategy for each cell and a restriction only on the cell that this strategy belongs to for intent in intents: nashUserStrategy[intent] = dict() for query in queries: nashUserStrategy[intent][query] = copy.deepcopy(userStrategy) for query2 in copy.deepcopy(queries): nashUserStrategy[intent][query][intent][query2] = Symbol('Nash'+str(len(nashUserStrategy))+str(len(nashUserStrategy[intent]))+'User['+intent + '][' + query2 + ']', REAL) letters.add(nashUserStrategy[intent][query][intent][query2]) if query == query2: nashRestrictions.append(NotEquals(nashUserStrategy[intent][query][intent][query2], userStrategy[intent][query2])) #Same here except for DBMS for query in queries: nashdbmsStrategy[query] = dict() for tup in tuples: nashdbmsStrategy[query][tup] = copy.deepcopy(dbmsStrategy) for tup2 in tuples: nashdbmsStrategy[query][tup][query][tup2] = Symbol('Nash'+str(len(nashdbmsStrategy))+str(len(nashdbmsStrategy[query]))+'DBMS['+query + '][' + tup2 + ']', REAL) letters.add(nashdbmsStrategy[query][tup][query][tup2]) if tup == tup2: nashRestrictions.append(NotEquals(nashdbmsStrategy[query][tup][query][tup2], dbmsStrategy[query][tup2])) #Tells the range of values, for now set to Pure strategy so [0,1] domains = And([Or(Equals(l, Real(0)), Equals(l, Real(1))) for l in letters]) if DEBUG: print('\nDomain Serialization: ') print(domains) #Adds restriction that no value from the nashStrategies can be the same as the corresponding cell they are testing. This ensures a 'move' nashProblem = And(nashRestrictions) if DEBUG: print('\nNash Domain Serialization: ') print(nashProblem) #This is doing all the row stochastic stuff allEquals = [] #Adds each row of the user strategy stochasticMatrixUser = list() stochasticMatrixNashUser = list() for intent in intents: stochasticMatrixUser.append(Plus(userStrategy[intent].values())) for query in queries: allEquals.append(Equals(Plus(nashUserStrategy[intent][query][intent].values()), Real(1))) #Adds each row of the DBMS strategy stochasticMatrixdbms = list() stochasticMatrixNashdbms = list() for query in queries: stochasticMatrixdbms.append(Plus(dbmsStrategy[query].values())) for tup in tuples: allEquals.append(Equals(Plus(nashdbmsStrategy[query][tup][query].values()), Real(1))) #Checks the rows to make sure that the strategies are row stochastic stochUserEquals = [Equals(x, Real(1)) for x in stochasticMatrixUser] stochdbmsEquals = [Equals(x, Real(1)) for x in stochasticMatrixdbms] allEquals += stochUserEquals + stochdbmsEquals #Stochastic problem stochasticProblem = And(set(allEquals)) if DEBUG: print('\nStochastic Serialization: ') print(stochasticProblem.serialize()) #Uses Formula 1 from my paper to calculate the payoff, assuming uniform prior. reward = list() for intent in intents: for query in queries: for tup in tuples: reward.append(Times(Real(1/len(intents)), userStrategy[intent][query], dbmsStrategy[query][tup], rewardMatrix[intent][tup])) #Reward problem, requires a minimum reward. May not always be possible to achieve this reward rewardProblem = GE(Plus(reward), Real(minRewardValue)) if DEBUG: print('\nReward Serialization: ') print(rewardProblem) nashRewardUser = dict() nashRewardDbms = dict() #Again using Formula 1, but now we are creating one for each move to make sure that it is less than (Strict Nash) or less than or equal (Nash) for strat in nashUserStrategy: if strat not in nashRewardUser: nashRewardUser[strat] = dict() for strat2 in nashUserStrategy[strat]: if strat2 not in nashRewardUser[strat]: nashRewardUser[strat][strat2] = list() for intent in intents: for query in queries: for tup in tuples: nashRewardUser[strat][strat2].append(Times(Real(1/len(intents)), nashUserStrategy[strat][strat2][intent][query], dbmsStrategy[query][tup], rewardMatrix[intent][tup])) #Same, but for DBMS side. Just separated them to make it cleaner for strat in nashdbmsStrategy: if strat not in nashRewardDbms: nashRewardDbms[strat] = dict() for strat2 in nashdbmsStrategy[strat]: if strat2 not in nashRewardDbms[strat]: nashRewardDbms[strat][strat2] = list() for intent in intents: for query in queries: for tup in tuples: nashRewardDbms[strat][strat2].append(Times(Real(1/len(intents)), userStrategy[intent][query], nashdbmsStrategy[strat][strat2][query][tup], rewardMatrix[intent][tup])) if DEBUG: print('\nReward Serialization Nash: ') #Check user nash #This is where we actually perform the checking to see if it is less than or equal (or prep to be added to the solver) userNash = list() for intent in intents: for query in queries: if strict: userNash.append(Implies(NotEquals(nashUserStrategy[intent][query][intent][query], userStrategy[intent][query]), LT(Plus(nashRewardUser[intent][query]), Plus(reward)))) else: userNash.append(Implies(NotEquals(nashUserStrategy[intent][query][intent][query], userStrategy[intent][query]), LE(Plus(nashRewardUser[intent][query]), Plus(reward)))) userNashProblem = And(userNash) if DEBUG: print(userNashProblem.serialize()) #Check dbms nash dbmsNash = list() for query in queries: for tup in tuples: if strict: dbmsNash.append(Implies(NotEquals(dbmsStrategy[query][tup], nashdbmsStrategy[query][tup][query][tup]), LE(Plus(nashRewardDbms[query][tup]), Plus(reward)))) else: dbmsNash.append(Implies(NotEquals(dbmsStrategy[query][tup], nashdbmsStrategy[query][tup][query][tup]), LE(Plus(nashRewardDbms[query][tup]), Plus(reward)))) dbmsNashProblem = And(dbmsNash) if DEBUG: print(dbmsNashProblem.serialize()) #Add each component to the solver and test as we go #PySMT has another method where you just AND everything together, but they suggest #this method as it looks cleaner and you can see which one fails, if any with Solver(name="z3") as solver: solver.add_assertion(domains) if not solver.solve(): print('No solultion available (domains)') return print('Can satisfy domains') solver.add_assertion(nashProblem) if not solver.solve(): print('No solultion available (nash restrictions)') return print('Can satisfy nash domains') solver.add_assertion(stochasticProblem) if not solver.solve(): print('No sulution available (stochastic)') return print('Can satisfy stochastic') if minReward: solver.add_assertion(rewardProblem) if not solver.solve(): print('No solution available') return print('Can satisfy minimum reward') solver.add_assertion(userNashProblem) if not solver.solve(): print('No solution available (user nash)') return print('Can satisfy user nash') solver.add_assertion(dbmsNashProblem) if not solver.solve(): print('No solution available (dbms nash)') return print('Can satisfy dbms nash') #Print out the final assignments if it made it this far, as a solution exists for intent in intents: for query in queries: print("%s = %s" % (userStrategy[intent][query], solver.get_value(userStrategy[intent][query]))) for query in queries: for tup in tuples: print("%s = %s" % (dbmsStrategy[query][tup], solver.get_value(dbmsStrategy[query][tup])))
def test_oracle(self): x = FreshSymbol(REAL) f = Equals(Times(x, x), Real(2)) logic = get_logic(f) self.assertFalse(logic.theory.linear)