def test_sampling(): domain = Domain.make(["a", "b"], ["x", "y"], real_bounds=(0, 1)) a, b, x, y = domain.get_symbols() support = (a | b) & (~a | ~b) & (x <= y) weight = smt.Ite(a, smt.Real(1), smt.Real(2)) required_sample_count = 10000 samples_weighted, pos_ratio = positive(required_sample_count, domain, support, weight) assert samples_weighted.shape[0] == required_sample_count assert sum(evaluate(domain, support, samples_weighted)) == len(samples_weighted) samples_a = sum(evaluate(domain, a, samples_weighted)) samples_b = sum(evaluate(domain, b, samples_weighted)) assert samples_a == pytest.approx(samples_b / 2, rel=0.2) assert pos_ratio == pytest.approx(0.25, rel=0.1) samples_unweighted, pos_ratio = positive(required_sample_count, domain, support) assert samples_unweighted.shape[0] == required_sample_count assert sum(evaluate(domain, support, samples_unweighted)) == len(samples_weighted) samples_a = sum(evaluate(domain, a, samples_unweighted)) samples_b = sum(evaluate(domain, b, samples_unweighted)) assert samples_a == pytest.approx(samples_b, rel=0.1) assert pos_ratio == pytest.approx(0.25, rel=0.1)
def get_bounds(self, formula_manager=None): fm = smt if formula_manager is None else formula_manager bounds = [] for v, (lb, ub) in self.var_domains.items(): symbol = self.get_symbol(v, formula_manager) if lb is not None: bounds.append(smt.LE(smt.Real(float(lb)), symbol)) if ub is not None: bounds.append(smt.LE(symbol, smt.Real(float(ub)))) return fm.And(*bounds)
def check_balancer_flow(junctions, upstream_densities, downstream_velocities): assert len(upstream_densities) == len(downstream_velocities) width = len(upstream_densities) length = max(x for x, _, _ in junctions) formula, belts = balancer_flow_formula(junctions, width, length) # Set initial conditions. for y, rho in enumerate(upstream_densities): formula.append(s.Equals(belts[y][0].rho, s.Real(rho))) for y, v in enumerate(downstream_velocities): formula.append(s.Equals(belts[y][-1].v, s.Real(v))) m = get_model_or_print_ucore(s.And(formula)) # Print output diagram. junctions_by_x = [[] for x in range(length + 1)] for (x, y1, y2) in junctions: junctions_by_x[x].append((y1, y2)) ud_width = max(len(str(ud)) for ud in upstream_densities) dv_width = max(len(str(dv)) for dv in downstream_velocities) rho_width = max( len(str(m.get_py_value(belt.rho))) for beltway in belts for belt in beltway) v_width = max( len(str(m.get_py_value(belt.v))) for beltway in belts for belt in beltway[1:-1]) for y, beltway in enumerate(belts): print(f'{str(upstream_densities[y]):{ud_width}s}>>>', end=':') for x, belt in enumerate(beltway): letter_map = {} if x < len(beltway) - 1: for (y1, y2), letter in zip(junctions_by_x[x + 1], 'abcdefghjkmn'): assert y1 not in letter_map assert y2 not in letter_map letter_map[y1] = letter letter_map[y2] = letter belt_status = f'{str(m.get_py_value(belt.rho)):>{rho_width}s}@{str(m.get_py_value(belt.v)):{v_width}s}' print(f' >>> {belt_status} >>>', end=f' {letter_map.get(y,"|")}') end_flux = m.get_py_value(beltway[-1].flux) print(f'> {end_flux}.') # The moment of truth... theoretical_throughput = min(sum(upstream_densities), sum(downstream_velocities)) print(f'Theoretical Q: {theoretical_throughput}') actual_throughput = sum( m.get_py_value(beltway[-1].flux) for beltway in belts) print(f'Actual Q: {actual_throughput}') if theoretical_throughput != actual_throughput: print( f'{float(1-(actual_throughput/theoretical_throughput)):.0%} slowdown :(' ) print('∎')
def univariate(n): domain = Domain.make([], ["x{}".format(i) for i in range(n)], real_bounds=(-2, 2)) x_vars = domain.get_symbols() support = smt.And(*[x > 0.5 for x in x_vars]) weight = smt.Times(*[smt.Ite((x > -1) & (x < 1), smt.Ite(x < 0, x + smt.Real(1), -x + smt.Real(1)), smt.Real(0)) for x in x_vars]) return FileDensity(domain, support, weight)
def find_hl(data, domain, active_indices, solver): # Constants n_r = len(domain.real_vars) real_features = [[row[v] for v in domain.real_vars] for row, _ in data] labels = [row[1] for row in data] # Variables a_r = [smt.Symbol("a_r[{}]".format(r), REAL) for r in range(n_r)] b = smt.Symbol("b", REAL) # Constraints for i in active_indices: x_r, label = real_features[i], labels[i] sum_coefficients = smt.Plus( [a_r[r] * smt.Real(x_r[r]) for r in range(n_r)]) if label: solver.add_assertion(sum_coefficients + DELTA <= b) else: solver.add_assertion(sum_coefficients - DELTA > b) if not solver.solve(): return None model = solver.get_model() x_vars = [domain.get_symbol(domain.real_vars[r]) for r in range(n_r)] return smt.Plus([model.get_value(a_r[r]) * x_vars[r] for r in range(n_r)]) <= model.get_value(b)
def walk_constant(self, value, v_type): if v_type == smt.BOOL: return smt.Bool(value) elif v_type == smt.REAL: return smt.Real(value) else: return ValueError("Unknown type {}".format(v_type))
def make_from_graph(graph): n = graph.vcount() domain = Domain.make([], [f"x{i}" for i in range(n)], real_bounds=(-1, 1)) X = domain.get_symbols() support = smt.And(*((X[e.source] + 1 <= X[e.target]) | (X[e.target] <= X[e.source] - 1) for e in graph.es)) return Density(domain, support & domain.get_bounds(), smt.Real(1))
def sympy2pysmt(expr, expr_type=None): if type(expr ) == Poly: # turn Poly instances into generic sympy expressions expr = expr.as_expr() op = type(expr) if len(expr.free_symbols) == 0: if expr.is_Boolean: return smt.Bool(bool(expr)) elif expr.is_number: return smt.Real(float(expr)) elif op == sym.Symbol: if expr_type is None: raise ValueError( "Can't create a pysmt Symbol without type information") return smt.Symbol(expr.name, expr_type) elif op in SYM2SMT: if expr_type is None: expr_type = OP_TYPE[op] smtargs = [sympy2pysmt(c, expr_type) for c in expr.args] return SYM2SMT[op](*smtargs) raise NotImplementedError(f"SYMPY -> PYSMT Not implemented for op: {op}")
def compute_volume(self, sample_count=None): sample_count = sample_count or self.sample_count volume = self.tree.get_volume(sample_count) if self.weight and self.weight != smt.Real(1): return self.tree.get_weighted_volume( self.weight) * self.tree.builder.volume else: return volume * self.tree.builder.volume
def test_convert_weight2(): domain = Domain.make(["a", "b"], ["x", "y"], [(0, 1), (0, 1)]) a, b, x, y = domain.get_symbols(domain.variables) ite_a = smt.Ite(a, smt.Real(0.6), smt.Real(0.4)) ite_b = smt.Ite(b, smt.Real(0.8), smt.Real(0.2)) ite_x = smt.Ite( x >= smt.Real(0.5), smt.Real(0.5) * x + smt.Real(0.1) * y, smt.Real(0.1) * x + smt.Real(0.7) * y, ) weight = ite_a * ite_b * ite_x algebra = PolynomialAlgebra() abstractions_c, var_to_lit_c = dict(), dict() converted_c = convert_function(smt.Real(0.6), SddManager(), algebra, abstractions_c, var_to_lit_c) for p, s in converted_c.sdd_dict.items(): print("{}: {}".format(p, recover_formula(s, abstractions_c, var_to_lit_c))) assert len(converted_c.sdd_dict) == 1 abstractions_a, var_to_lit_a = dict(), dict() converted_a = convert_function(ite_a, SddManager(), algebra, abstractions_a, var_to_lit_a) for p, s in converted_a.sdd_dict.items(): print("{}: {}".format(p, recover_formula(s, abstractions_a, var_to_lit_a))) assert len(converted_a.sdd_dict) == 2 converted_b = convert_function(ite_b, SddManager(), algebra) assert len(converted_b.sdd_dict) == 2 print("X") abstractions_x, var_to_lit_x = dict(), dict() converted_x = convert_function(ite_x, SddManager(), algebra, abstractions_x, var_to_lit_x) for p, s in converted_x.sdd_dict.items(): print("{}: {}".format(p, recover_formula(s, abstractions_x, var_to_lit_x))) assert len(converted_x.sdd_dict) == 2 converted = convert_function(weight, SddManager(), algebra) assert len(converted.sdd_dict) == 2 * 2 * 2
def test_rejection_iff_real(): domain = Domain.make([], ["x", "y"], real_bounds=(-1, 1)) x, y = domain.get_symbols() c = 0.00000001 f1 = (x * c > 0) & (x * c <= y * c) & (y * c < c) f2 = normalize_formula(f1) rej_vol1 = RejectionEngine(domain, (f1 | f2) & (~f1 | ~f2), smt.Real(1.0), 100000).compute_volume() rej_vol2 = RejectionEngine(domain, smt.Iff(f1, ~f2), smt.Real(1.0), 100000).compute_volume() rej_vol3 = RejectionEngine(domain, ~smt.Iff(f1, f2), smt.Real(1.0), 100000).compute_volume() assert rej_vol1 == pytest.approx(0, REL_ERROR**3) assert rej_vol2 == pytest.approx(0, REL_ERROR**3) assert rej_vol3 == pytest.approx(0, REL_ERROR**3)
def test_xadd_iff_real(): domain = Domain.make([], ["x", "y"], real_bounds=(-1, 1)) x, y = domain.get_symbols() c = 0.00000001 f1 = (x * c > 0) & (x * c <= y * c) & (y * c < c) f2 = normalize_formula(f1) xadd_vol1 = XaddEngine(domain, (f1 | f2) & (~f1 | ~f2), smt.Real(1.0)).compute_volume() xadd_vol2 = XaddEngine(domain, smt.Iff(f1, ~f2), smt.Real(1.0)).compute_volume() xadd_vol3 = XaddEngine(domain, ~smt.Iff(f1, f2), smt.Real(1.0)).compute_volume() assert xadd_vol1 == pytest.approx(0, EXACT_REL_ERROR) assert xadd_vol2 == pytest.approx(0, EXACT_REL_ERROR) assert xadd_vol3 == pytest.approx(0, EXACT_REL_ERROR)
def compute_probability(self, query, sample_count=None): sample_count = sample_count or self.sample_count volume = self.tree.get_volume(sample_count) if self.weight and self.weight != smt.Real(1): return self.tree.get_weighted_volume( self.weight, query) / self.tree.get_weighted_volume( self.weight) else: return self.tree.get_volume(query=query) / volume
def generate_half_space_sample(domain, real_count): samples = [get_sample(domain) for _ in range(real_count)] coefficients, offset = Learner.fit_hyperplane(domain, samples) coefficients = [smt.Real(float(coefficients[i][0])) * domain.get_symbol(domain.real_vars[i]) for i in range(real_count)] if random.random() < 0.5: return smt.Plus(*coefficients) <= offset else: return smt.Plus(*coefficients) >= offset
def ast_to_smt(self, node): """ :type node: Node """ def convert_children(number=None): if number is not None and len(node.children) != number: raise Exception( "The number of children ({}) differed from {}".format( len(node.children), number)) return [self.ast_to_smt(child) for child in node.children] if node.name == "ite": return smt.Ite(*convert_children(3)) elif node.name == "~": return smt.Not(*convert_children(1)) elif node.name == "^": return smt.Pow(*convert_children(2)) elif node.name == "&": return smt.And(*convert_children()) elif node.name == "|": return smt.Or(*convert_children()) elif node.name == "*": return smt.Times(*convert_children()) elif node.name == "+": return smt.Plus(*convert_children()) elif node.name == "-": return smt.Minus(*convert_children(2)) elif node.name == "<=": return smt.LE(*convert_children(2)) elif node.name == ">=": return smt.GE(*convert_children(2)) elif node.name == "<": return smt.LT(*convert_children(2)) elif node.name == ">": return smt.GT(*convert_children(2)) elif node.name == "=": return smt.Equals(*convert_children(2)) elif node.name == "const": c_type, c_value = [child.name for child in node.children] if c_type == "bool": return smt.Bool(bool(c_value)) elif c_type == "real": return smt.Real(float(c_value)) else: raise Exception("Unknown constant type {}".format(c_type)) elif node.name == "var": v_type, v_name = [child.name for child in node.children] if v_type == "bool": v_smt_type = smt.BOOL elif v_type == "real": v_smt_type = smt.REAL else: raise Exception("Unknown variable type {}".format(v_type)) return smt.Symbol(v_name, v_smt_type) else: raise RuntimeError("Unrecognized node type '{}'".format(node.name))
def generate_click_graph(n): def t(c): return smt.Ite(c, one, zero) sim_n, cl_n, b_n, sim_x_n, b_x_n = "sim", "cl", "b", "sim_x", "b_x" domain = Domain.make( # Boolean ["{}_{}".format(sim_n, i) for i in range(n)] + ["{}_{}_{}".format(cl_n, i, j) for i in range(n) for j in (0, 1)] + ["{}_{}_{}".format(b_n, i, j) for i in range(n) for j in (0, 1)], # Real ["{}".format(sim_x_n)] + ["{}_{}_{}".format(b_x_n, i, j) for i in range(n) for j in (0, 1)], real_bounds=(0, 1)) sim = [domain.get_symbol("{}_{}".format(sim_n, i)) for i in range(n)] cl = [[domain.get_symbol("{}_{}_{}".format(cl_n, i, j)) for j in (0, 1)] for i in range(n)] b = [[domain.get_symbol("{}_{}_{}".format(b_n, i, j)) for j in (0, 1)] for i in range(n)] sim_x = domain.get_symbol("{}".format(sim_x_n)) b_x = [[domain.get_symbol("{}_{}_{}".format(b_x_n, i, j)) for j in (0, 1)] for i in range(n)] support = smt.And([ smt.Iff(cl[i][0], b[i][0]) & smt.Iff(cl[i][1], (sim[i] & b[i][0]) | (~sim[i] & b[i][1])) for i in range(n) ]) one = smt.Real(1) zero = smt.Real(0) w_sim_x = t(sim_x >= 0) * t(sim_x <= 1) w_sim = [smt.Ite(s_i, sim_x, 1 - sim_x) for s_i in sim] w_b_x = [ t(b_x[i][j] >= 0) * t(b_x[i][j] <= 1) for i in range(n) for j in (0, 1) ] w_b = [ smt.Ite(b[i][j], b_x[i][j], 1 - b_x[i][j]) for i in range(n) for j in (0, 1) ] weight = smt.Times(*([w_sim_x] + w_sim + w_b_x + w_b)) return Density(domain, support, weight)
def dual(n): n = 2*n domain = Domain.make([], ["x{}".format(i) for i in range(n)], real_bounds=(0, 1)) x_vars = domain.get_symbols() terms = [x_vars[2 * i] <= x_vars[2 * i + 1] for i in range(int(n / 2))] disjunction = smt.Or(*terms) for i in range(len(terms)): for j in range(i + 1, len(terms)): disjunction &= ~terms[i] | ~terms[j] return FileDensity(domain, disjunction & domain.get_bounds(), smt.Real(1))
def test_rejection_iff_bool(): domain = Domain.make(["a", "b"]) a, b = domain.get_symbols() print(domain) vol_t = RejectionEngine(domain, smt.TRUE(), smt.Real(1.0), 100000).compute_volume() vol1 = RejectionEngine(domain, (a | b) & (~a | ~b), smt.Real(1.0), 100000).compute_volume() vol2 = RejectionEngine(domain, smt.Iff(a, ~b), smt.Real(1.0), 100000).compute_volume() vol3 = RejectionEngine(domain, ~smt.Iff(a, b), smt.Real(1.0), 100000).compute_volume() print(vol1, vol2, vol3, vol_t) # print(PredicateAbstractionEngine(domain, a | b, smt.Real(1.0)).compute_volume()) print(XaddEngine(domain, a | b, smt.Real(1.0)).compute_volume()) quit()
def generate_xor(n): domain = make_domain(n) symbols = domain.get_symbols(domain.real_vars) x, symbols = symbols[0], symbols[1:] bounds = make_distinct_bounds(domain) terms = [x <= v for v in symbols] xor = smt.FALSE() for term in terms: xor = (xor | term) & ~(xor & term) return Density(flip_domain(domain), bounds & xor, smt.Real(1.0))
def import_smt_synthetic(filename): # type: (str) -> Density with open(filename) as f: flat = json.load(f) domain = Domain.from_state(flat["synthetic_problem"]["problem"]["domain"]) queries = [smt.TRUE()] support = nested_to_smt(flat["synthetic_problem"]["problem"]["theory"]) & domain.get_bounds() weights = smt.Real(1) return Density(domain, support, weights, queries)
def xor(n): domain = make_domain(n) symbols = domain.get_symbols(domain.real_vars) x, symbols = symbols[0], symbols[1:] bounds = make_distinct_bounds(domain) terms = [x <= v for v in symbols] xor = smt.FALSE() for term in terms: xor = (xor | term) & ~(xor & term) flipped_domain = Domain(list(reversed([v for v in domain.variables if v != "x"])) + ["x"], domain.var_types, domain.var_domains) return FileDensity(flipped_domain, bounds & xor, smt.Real(1.0))
def test_nested(): formula = (smt.Symbol("x", smt.REAL) * smt.Real(2) + smt.Real(5.125)) * smt.Real(-1.25) positive = (smt.Symbol("x", smt.REAL) * smt.Real(2) + smt.Real(5.125)) * smt.Real(1.25) result = make_coefficients_positive(formula) assert Polynomial.from_smt(positive) == Polynomial.from_smt(result)
def __encodeTerminal(symbolicExpression, type): if isinstance(symbolicExpression, sympy.Symbol): if type.literal == 'Integer': return pysmt.Symbol(symbolicExpression.name, pysmt.INT) elif type.literal == 'Real': return pysmt.Symbol(symbolicExpression.name, pysmt.REAL) elif type.literal == 'Bool': return pysmt.Symbol(symbolicExpression.name, pysmt.BOOL) else: # type.literal == 'BitVector' return pysmt.Symbol(symbolicExpression.name, pysmt.BVType(type.size)) else: if type.literal == 'Integer': return pysmt.Int(symbolicExpression.p) elif type.literal == 'Real': if isinstance(symbolicExpression, sympy.Rational): return pysmt.Real(symbolicExpression.p / symbolicExpression.q) else: # isinstance(symbolicExpression, sympy.Float) return pysmt.Real(symbolicExpression) elif type.literal == 'Bool': return pysmt.Bool(symbolicExpression) else: # type.literal == 'BitVector' return pysmt.BV(symbolicExpression, type.size)
def test_pa_iff_real(): pytest.skip("Bug fix requires changing PA solver") domain = Domain.make([], ["x", "y"], real_bounds=(-1, 1)) x, y = domain.get_symbols() c = 0.00000001 f1 = (x * c >= 0) & (x * c <= y * c) & (y * c < c) f2 = normalize_formula(f1) print(smt_to_nested(f2)) pa_vol1 = PredicateAbstractionEngine( domain, domain.get_bounds() & (f1 | f2) & (~f1 | ~f2), smt.Real(1.0)).compute_volume() smt.write_smtlib(domain.get_bounds() & (f1 | f2) & (~f1 | ~f2), "test_pa_iff_real.support") smt.write_smtlib(smt.Real(1.0), "test_pa_iff_real.weight") pa_vol2 = PredicateAbstractionEngine(domain, smt.Iff(f1, ~f2), smt.Real(1.0)).compute_volume() pa_vol3 = PredicateAbstractionEngine(domain, ~smt.Iff(f1, f2), smt.Real(1.0)).compute_volume() assert pa_vol1 == pytest.approx(0, REL_ERROR**3) assert pa_vol2 == pytest.approx(0, REL_ERROR**3) assert pa_vol3 == pytest.approx(0, REL_ERROR**3)
def generate_mutual_exclusive(n): domain = make_domain(n) symbols = domain.get_symbols(domain.real_vars) x, symbols = symbols[0], symbols[1:] bounds = make_distinct_bounds(domain) terms = [x <= v for v in symbols] disjunction = smt.Or(*terms) for i in range(n): for j in range(i + 1, n): disjunction &= ~terms[i] | ~terms[j] return Density(flip_domain(domain), bounds & disjunction, smt.Real(1.0))
def dual_paths(n): booleans = [] # ["A{}".format(i) for i in range(n)] domain = Domain.make(booleans, ["x{}".format(i) for i in range(n)], real_bounds=(0, 1)) bool_vars = domain.get_bool_symbols() real_vars = domain.get_real_symbols() terms = [] for i in range(n): v1, v2 = random.sample(real_vars, 2) terms.append(v1 * random.random() <= v2 * random.random()) paths = [] for i in range(n): paths.append(smt.And(*random.sample(bool_vars + terms, n))) return Density(domain, domain.get_bounds() & smt.Or(*paths), smt.Real(1))
def mutual_exclusive(n): domain = make_domain(n) symbols = domain.get_symbols(domain.real_vars) x, symbols = symbols[0], symbols[1:] bounds = make_distinct_bounds(domain) terms = [x <= v for v in symbols] disjunction = smt.TRUE() for i in range(n): for j in range(i + 1, n): disjunction &= ~terms[i] | ~terms[j] disjunction = smt.simplify(disjunction) & smt.Or(*terms) flipped_domain = Domain(list(reversed([v for v in domain.variables if v != "x"])) + ["x"], domain.var_types, domain.var_domains) return FileDensity(flipped_domain, disjunction & bounds, smt.Real(1.0))
def import_wmi_mspn(filename): # type: (str) -> Density q_file, s_file, w_file = ("{}.{}".format(filename, ext) for ext in ["query", "support", "weight"]) queries = [smt.TRUE()] if not os.path.exists(q_file) else [smt.read_smtlib(q_file)] support = smt.read_smtlib(s_file) if os.path.exists(w_file): weights = smt.read_smtlib(w_file) else: weights = smt.Real(1) name = os.path.basename(filename) parts = name.split("_") real_vars = int(parts[1]) bool_vars = int(parts[2]) domain = Domain.make(["A_{}".format(i) for i in range(bool_vars)], {"x_{}".format(i): [0, 1] for i in range(real_vars)}) return Density(domain, support, weights, queries)
def accuracy_approx(experiment): key = "accuracy_approx:{}".format(experiment.imported_from_file) if Properties.db.exists(key): return Properties.db.get(key) else: pysmt.environment.push_env() pysmt.environment.get_env().enable_infix_notation = True if os.path.basename(experiment.imported_from_file).startswith("synthetic"): db = Properties.get_db_synthetic(experiment) name = Properties.to_synthetic_name(experiment.imported_from_file) entry = db.get(name) domain = import_domain(json.loads(entry["domain"])) true_formula = nested_to_smt(entry["formula"]) else: density = Density.import_from(experiment.parameters.original_values["domain"]) domain = Domain(density.domain.variables, density.domain.var_types, Properties.get_bound(experiment)) true_formula = density.support learned_formula = nested_to_smt(experiment.results.formula) engine = RejectionEngine(domain, smt.TRUE(), smt.Real(1.0), 100000) accuracy = engine.compute_probability(smt.Iff(true_formula, learned_formula)) pysmt.environment.pop_env() print(accuracy) Properties.db.set(key, accuracy) return accuracy
def power(self, a, power): return smt.Pow(a, smt.Real(power))