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 formula(self, p): ptyp = type(p) if ptyp is BD.GTZ or ptyp is BD.GEZ or ptyp is BD.EQZ: aform = self.affine(p.expr) if ptyp is BD.GTZ: return SMT.GT(aform, SMT.Int(0)) elif ptyp is BD.GEZ: return SMT.GE(aform, SMT.Int(0)) elif ptyp is BD.EQZ: return SMT.Equals(aform, SMT.Int(0)) else: assert False elif ptyp is BD.Rel: rsym = self._ctxt.get(p.name) assert rsym is not None, f"expected relation name '{p.name}'" args = [] for a in p.args: sym = self._ctxt.get(a) assert sym is not None, f"expected variable name '{a}'" args.append(sym) return SMT.Function(rsym, args) elif ptyp is BD.Conj or ptyp is BD.Disj: lhs = self.formula(p.lhs) rhs = self.formula(p.rhs) smtop = SMT.And if ptyp is BD.Conj else SMT.Or return smtop(lhs, rhs)
def __getitem__(self, index): size = self.size if isinstance(index, slice): start, stop, step = index.start, index.stop, index.step if start is None: start = 0 elif start < 0: start = size + start if stop is None: stop = size elif stop < 0: stop = size + stop stop = min(stop, size) if step is None: step = 1 elif step != 1: raise IndexError('SMT extract does not support step != 1') v = self.value[start:stop - 1] return type(self).unsized_t[v.get_type().width](v) elif isinstance(index, int): if index < 0: index = size + index if not (0 <= index < size): raise IndexError() v = self.value[index] return self.get_family().Bit(smt.Equals(v, smt.BV(1, 1))) else: raise TypeError()
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 test_bv2pysmt(self): bvx, bvy = Variable("x", 8), Variable("y", 8) psx, psy = bv2pysmt(bvx), bv2pysmt(bvy) self.assertEqual(bv2pysmt(Constant(0, 8)), sc.BV(0, 8)) self.assertEqual(psx, sc.Symbol("x", typing.BVType(8))) self.assertEqual(bv2pysmt(~bvx), sc.BVNot(psx)) self.assertEqual(bv2pysmt(bvx & bvy), sc.BVAnd(psx, psy)) self.assertEqual(bv2pysmt(bvx | bvy), sc.BVOr(psx, psy)) self.assertEqual(bv2pysmt(bvx ^ bvy), sc.BVXor(psx, psy)) self.assertEqual(bv2pysmt(BvComp(bvx, bvy)), sc.Equals(psx, psy)) self.assertEqual(bv2pysmt(BvNot(BvComp(bvx, bvy))), sc.Not(sc.Equals(psx, psy))) self.assertEqual(bv2pysmt(bvx < bvy), sc.BVULT(psx, psy)) self.assertEqual(bv2pysmt(bvx <= bvy), sc.BVULE(psx, psy)) self.assertEqual(bv2pysmt(bvx > bvy), sc.BVUGT(psx, psy)) self.assertEqual(bv2pysmt(bvx >= bvy), sc.BVUGE(psx, psy)) self.assertEqual(bv2pysmt(bvx << bvy), sc.BVLShl(psx, psy)) self.assertEqual(bv2pysmt(bvx >> bvy), sc.BVLShr(psx, psy)) self.assertEqual(bv2pysmt(RotateLeft(bvx, 1)), sc.BVRol(psx, 1)) self.assertEqual(bv2pysmt(RotateRight(bvx, 1)), sc.BVRor(psx, 1)) self.assertEqual(bv2pysmt(bvx[4:2]), sc.BVExtract(psx, 2, 4)) self.assertEqual(bv2pysmt(Concat(bvx, bvy)), sc.BVConcat(psx, psy)) # zeroextend reduces to Concat # self.assertEqual(bv2pysmt(ZeroExtend(bvx, 2)), sc.BVZExt(psx, 2)) self.assertEqual(bv2pysmt(Repeat(bvx, 2)), psx.BVRepeat(2)) self.assertEqual(bv2pysmt(-bvx), sc.BVNeg(psx)) self.assertEqual(bv2pysmt(bvx + bvy), sc.BVAdd(psx, psy)) # bvsum reduces to add # self.assertEqual(bv2pysmt(bvx - bvy), sc.BVSub(psx, psy)) self.assertEqual(bv2pysmt(bvx * bvy), sc.BVMul(psx, psy)) self.assertEqual(bv2pysmt(bvx / bvy), sc.BVUDiv(psx, psy)) self.assertEqual(bv2pysmt(bvx % bvy), sc.BVURem(psx, psy))
def walk_equals(self, left, right): return smt.Equals(self.walk_smt(left), self.walk_smt(right))
def balancer_flow_formula(junctions, width, length): formula = [] belts = [[ Belt(s.Symbol(f'b{i}[{x}].rho', t.REAL), s.Symbol(f'b{i}[{x}].v', t.REAL)) for x in range(length + 1) ] for i in range(width)] for beltway in belts: for belt in beltway: formula.extend(domain(belt)) if not s.is_sat(s.And(formula)): raise Exception('Domain is not SAT :/') # Balancing rules. junctions_by_x = [[] for x in range(length + 1)] for (x, y1, y2) in junctions: junctions_by_x[x].append((y1, y2)) inn = x - 1 out = x input_rho = s.Plus(belts[y1][inn].rho, belts[y2][inn].rho) # We want to put half of the input on each output. half_input = s.Div(input_rho, s.Real(2)) # If output velocity is less than the half_input that we would like to # assign to it, we've filled it. Velocity is a hard limit, because it's out # of influence of this splitter. We'll set the output density to 1 in that # case. Aside: The flux is the min of that and the velocity, so if # out-velocity is the limiting factor, it won't change the flux calculation # to just assign rho_out = v_out. # # Now, the excess that we couldn't assign has to go somewhere: (1) to the # other output belt; if that's full, (2) feed back up the chain by reducing # input velocities. excess_from_1 = s.Max(s.Real(0), s.Minus(half_input, belts[y1][out].v)) excess_from_2 = s.Max(s.Real(0), s.Minus(half_input, belts[y2][out].v)) # This formula is most accurate for assignment > velocity (density will # equal 1), but it doesn't change the flux calculation just toset rho to # the velocity when velocity limits flow. (So you should be able to replace # the Ite by v_out and be OK.) formula.append( s.Equals( belts[y1][out].rho, s.Ite(half_input + excess_from_2 > belts[y1][out].v, s.Real(1), half_input + excess_from_2))) formula.append( s.Equals( belts[y2][out].rho, s.Ite(half_input + excess_from_1 > belts[y2][out].v, s.Real(1), half_input + excess_from_1))) output_v = s.Plus(belts[y1][out].v, belts[y2][out].v) half_output = s.Div(output_v, s.Real(2)) unused_density_from_1 = s.Max(s.Real(0), s.Minus(half_output, belts[y1][inn].rho)) unused_density_from_2 = s.Max(s.Real(0), s.Minus(half_output, belts[y2][inn].rho)) formula.append( s.Equals( belts[y1][inn].v, s.Ite(half_output + unused_density_from_2 > belts[y1][inn].rho, s.Real(1), half_output + unused_density_from_2))) formula.append( s.Equals( belts[y2][inn].v, s.Ite(half_output + unused_density_from_1 > belts[y2][inn].rho, s.Real(1), half_output + unused_density_from_1))) # Conservation of flux at each junction. input_flux = s.Plus(belts[y1][inn].flux, belts[y2][inn].flux) output_flux = s.Plus(belts[y1][out].flux, belts[y2][out].flux) formula.append(s.Equals(input_flux, output_flux)) # Any belts not involved in a junction are pass-throughs. Their successive # values must remain equal. thru_belts = [ list( set(range(width)) - {y1 for y1, y2 in junctions_by_x[x]} - {y2 for y1, y2 in junctions_by_x[x]}) for x in range(length + 1) ] for x, thru in enumerate(thru_belts[1:]): for y in thru: formula.append(s.Equals(belts[y][x].rho, belts[y][x + 1].rho)) formula.append(s.Equals(belts[y][x].v, belts[y][x + 1].v)) return formula, belts
def prove_properties(junctions): width = max(max(y1 for _, y1, _ in junctions), max(y2 for _, _, y2 in junctions)) + 1 length = max(x for x, _, _ in junctions) formula, belts = balancer_flow_formula(junctions, width, length) supply = s.Plus(beltway[0].rho for beltway in belts) demand = s.Plus(beltway[-1].v for beltway in belts) max_theoretical_throughput = s.Min(supply, demand) actual_throughput = s.Plus(beltway[-1].flux for beltway in belts) fully_balanced_output = [] max_flux = s.Max(b[-1].flux for b in belts) for i, b1 in enumerate(belts): fully_balanced_output.append( s.Or(s.Equals(b1[-1].flux, max_flux), s.Equals(b1[-1].flux, b1[-1].v))) fully_balanced_output = s.And(fully_balanced_output) fully_balanced_input = [] max_flux = s.Max(b[0].flux for b in belts) for i, b1 in enumerate(belts): fully_balanced_input.append( s.Or(s.Equals(b1[0].flux, max_flux), s.Equals(b1[0].flux, b1[0].rho))) fully_balanced_input = s.And(fully_balanced_input) with s.Solver() as solver: solver.add_assertion(s.And(formula)) solver.push() solver.add_assertion( s.Not(s.Equals(max_theoretical_throughput, actual_throughput))) if not solver.solve(): print("It's throughput-unlimited!") else: print("It's throughput-limited; here's an example:") m = solver.get_model() inputs = tuple(m.get_py_value(beltway[0].rho) for beltway in belts) outputs = tuple(m.get_py_value(beltway[-1].v) for beltway in belts) print(f'Input lane densities: ({", ".join(map(str, inputs))})') print(f'Output lane velocities: ({", ".join(map(str, outputs))})') check_balancer_flow(junctions, inputs, outputs) solver.pop() solver.push() solver.add_assertion(s.Not(fully_balanced_input)) if not solver.solve(): print("It's input-balanced!") else: print("It's input-imbalanced; here's an example:") m = solver.get_model() inputs = tuple(m.get_py_value(beltway[0].rho) for beltway in belts) outputs = tuple(m.get_py_value(beltway[-1].v) for beltway in belts) print(f'Input lane densities: ({", ".join(map(str, inputs))})') print(f'Output lane velocities: ({", ".join(map(str, outputs))})') check_balancer_flow(junctions, inputs, outputs) solver.pop() solver.push() solver.add_assertion(s.Not(fully_balanced_output)) if solver.solve: print("It's output-balanced!") else: print("It's output-imbalanced; here's an example:") m = solver.get_model() inputs = tuple(m.get_py_value(beltway[0].rho) for beltway in belts) outputs = tuple(m.get_py_value(beltway[-1].v) for beltway in belts) print(f'Input lane densities: ({", ".join(map(str, inputs))})') print(f'Output lane velocities: ({", ".join(map(str, outputs))})') check_balancer_flow(junctions, inputs, outputs)
print(variables) constraints = [] for var in variables: string = var.__repr__() # add requirement that all a_i, c_i > -1 if string[0] == "a" or string[0] == "c": constraints.append(SMT.GT(var, SMT.Int(-1))) # add requirement that b_i > 0 else: constraints.append(SMT.GT(var, SMT.Int(0))) for i in range(0, 3 * cap, 3): # add requirement that either a_i =0 or c_i = 0 constraints.append( SMT.Or(SMT.Equals(variables[i], SMT.Int(0)), SMT.Equals(variables[i + 2], SMT.Int(0)))) # add requirement that if a_i = 0 and c_i = 0 then b_i = 1 constraints.append( SMT.Or( SMT.Or(SMT.GT(variables[i], SMT.Int(0)), SMT.GT(variables[i + 2], SMT.Int(0))), SMT.Equals(variables[i + 1], SMT.Int(1)))) solver = SMT.Solver(name="z3") print("constraints:") for c in constraints: print(c) solver.add_assertion(c) # add equations
def bveq(self, other): return self.get_family().Bit(smt.Equals(self.value, other.value))
def learn_partial(self, solver, domain: Domain, data: np.ndarray, labels: np.ndarray, new_active_indices: Set): # Constants n_b_original = len(domain.bool_vars) n_b = n_b_original * 2 n_r = len(domain.real_vars) n_h_original = self.half_space_count if n_r > 0 else 0 n_h = n_h_original * 2 if self.allow_negations else n_h_original n_c = self.conjunction_count n_d = data.shape[0] real_indices = np.array( [domain.var_types[v] == smt.REAL for v in domain.variables]) real_features = data[:, real_indices] bool_features = data[:, np.logical_not(real_indices)] # Variables a_hr = [[ smt.Symbol("a_hr[{}][{}]".format(h, r), REAL) for r in range(n_r) ] for h in range(n_h_original)] b_h = [ smt.Symbol("b_h[{}]".format(h), REAL) for h in range(n_h_original) ] s_ch = [[smt.Symbol("s_ch[{}][{}]".format(c, h)) for h in range(n_h)] for c in range(n_c)] s_cb = [[smt.Symbol("s_cb[{}][{}]".format(c, b)) for b in range(n_b)] for c in range(n_c)] # Aux variables s_ih = [[smt.Symbol("s_ih[{}][{}]".format(i, h)) for h in range(n_h)] for i in range(n_d)] s_ic = [[smt.Symbol("s_ic[{}][{}]".format(i, c)) for c in range(n_c)] for i in range(n_d)] def pair(real: bool, c: int, index: int) -> Tuple[FNode, FNode]: if real: return s_ch[c][index], s_ch[c][index + n_h_original] else: return s_cb[c][index], s_cb[c][index + n_b_original] def order_equal(pair1, pair2): x_t, x_f, y_t, y_f = pair1 + pair2 return smt.Iff(x_t, y_t) & smt.Iff(x_f, y_f) def order_geq(pair1, pair2): x_t, x_f, y_t, y_f = pair1 + pair2 return x_t | y_f | ((~x_f) & (~y_t)) def pairs(c: int) -> List[Tuple[FNode, FNode]]: return [pair(True, c, i) for i in range(n_h_original) ] + [pair(False, c, i) for i in range(n_b_original)] def order_geq_lex(c1: int, c2: int): pairs_c1, pairs_c2 = pairs(c1), pairs(c2) assert len(pairs_c1) == len(pairs_c2) constraints = smt.TRUE() for j in range(len(pairs_c1)): condition = smt.TRUE() for i in range(j): condition &= order_equal(pairs_c1[i], pairs_c2[i]) constraints &= smt.Implies(condition, order_geq(pairs_c1[j], pairs_c2[j])) return constraints # Constraints for i in new_active_indices: x_r, x_b, label = [float(val) for val in real_features[i] ], bool_features[i], labels[i] for h in range(n_h_original): sum_coefficients = smt.Plus( [a_hr[h][r] * smt.Real(x_r[r]) for r in range(n_r)]) solver.add_assertion( smt.Iff(s_ih[i][h], sum_coefficients <= b_h[h])) for h in range(n_h_original, n_h): solver.add_assertion( smt.Iff(s_ih[i][h], ~s_ih[i][h - n_h_original])) for c in range(n_c): solver.add_assertion( smt.Iff( s_ic[i][c], smt. Or([smt.FALSE()] + [(s_ch[c][h] & s_ih[i][h]) for h in range(n_h)] + [s_cb[c][b] for b in range(n_b_original) if x_b[b]] + [ s_cb[c][b] for b in range(n_b_original, n_b) if not x_b[b - n_b_original] ]))) # --- [start] symmetry breaking --- # Mutually exclusive if "m" in self.symmetries: for c in range(n_c): for h in range(n_h_original): solver.add_assertion(~(s_ch[c][h] & s_ch[c][h + n_h_original])) for b in range(n_b_original): solver.add_assertion(~(s_cb[c][b] & s_cb[c][b + n_b_original])) # Normalized if "n" in self.symmetries: for h in range(n_h_original): solver.add_assertion( smt.Equals(b_h[h], smt.Real(1.0)) | smt.Equals(b_h[h], smt.Real(0.0))) # Vertical symmetries if "v" in self.symmetries: for c in range(n_c - 1): solver.add_assertion(order_geq_lex(c, c + 1)) # Horizontal symmetries if "h" in self.symmetries: for h in range(n_h_original - 1): solver.add_assertion(a_hr[h][0] >= a_hr[h + 1][0]) # --- [end] symmetry breaking --- if label: solver.add_assertion(smt.And([s_ic[i][c] for c in range(n_c)])) else: solver.add_assertion(smt.Or([~s_ic[i][c] for c in range(n_c)])) solver.solve() model = solver.get_model() x_vars = [domain.get_symbol(domain.real_vars[r]) for r in range(n_r)] half_spaces = [ smt.Plus( [model.get_value(a_hr[h][r]) * x_vars[r] for r in range(n_r)]) <= model.get_value(b_h[h]) for h in range(n_h_original) ] + [ smt.Plus( [model.get_value(a_hr[h][r]) * x_vars[r] for r in range(n_r)]) > model.get_value(b_h[h]) for h in range(n_h - n_h_original) ] b_vars = [ domain.get_symbol(domain.bool_vars[b]) for b in range(n_b_original) ] bool_literals = [b_vars[b] for b in range(n_b_original)] bool_literals += [~b_vars[b] for b in range(n_b - n_b_original)] conjunctions = [[ half_spaces[h] for h in range(n_h) if model.get_py_value(s_ch[c][h]) ] + [ bool_literals[b] for b in range(n_b) if model.get_py_value(s_cb[c][b]) ] for c in range(n_c)] return smt.And([smt.Or(conjunction) for conjunction in conjunctions])
def __getExpressionTree(symbolicExpression): # TODO LATER: take into account bitwise shift operations args = [] castType = None if len(symbolicExpression.args) > 0: for symbolicArg in symbolicExpression.args: arg, type = Solver.__getExpressionTree(symbolicArg) args.append(arg) if castType is None: castType = type else: if castType.literal == 'Integer': if type.literal == 'Real': castType = type # TODO LATER: consider other possible castings if castType.literal == 'Real': for i in range(len(args)): args[i] = pysmt.ToReal(args[i]) if isinstance(symbolicExpression, sympy.Not): if castType.literal == 'Integer': return pysmt.Equals(args[0], pysmt.Int(0)), Type('Bool') elif castType.literal == 'Real': return pysmt.Equals(args[0], pysmt.Real(0)), Type('Bool') elif castType.literal == 'Bool': return pysmt.Not(args[0]), Type('Bool') else: # castType.literal == 'BitVector' return pysmt.BVNot(args[0]), Type('BitVector') elif isinstance(symbolicExpression, sympy.Lt): return pysmt.LT(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Gt): return pysmt.GT(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Ge): return pysmt.GE(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Le): return pysmt.LE(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Eq): return pysmt.Equals(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.Ne): return pysmt.NotEquals(args[0], args[1]), Type('Bool') elif isinstance(symbolicExpression, sympy.And): if castType.literal == 'Bool': return pysmt.And(args[0], args[1]), Type('Bool') else: # type.literal == 'BitVector' return pysmt.BVAnd(args[0], args[1]), castType elif isinstance(symbolicExpression, sympy.Or): if castType.literal == 'Bool': return pysmt.Or(args[0], args[1]), Type('Bool') else: # type.literal == 'BitVector' return pysmt.BVOr(args[0], args[1]), castType elif isinstance(symbolicExpression, sympy.Xor): return pysmt.BVXor(args[0], args[1]), castType elif isinstance(symbolicExpression, sympy.Add): return pysmt.Plus(args), castType elif isinstance(symbolicExpression, sympy.Mul): return pysmt.Times(args), castType elif isinstance(symbolicExpression, sympy.Pow): return pysmt.Pow(args[0], args[1]), castType # TODO LATER: deal with missing modulo operator from pysmt else: if isinstance(symbolicExpression, sympy.Symbol): symbolType = Variable.symbolTypes[symbolicExpression.name] literal = symbolType.getTypeForSolver() designator = symbolType.designatorExpr1 type = Type(literal, designator) return Solver.__encodeTerminal(symbolicExpression, type), type elif isinstance(symbolicExpression, sympy.Integer): type = Type('Integer') return Solver.__encodeTerminal(symbolicExpression, type), type elif isinstance(symbolicExpression, sympy.Rational): type = Type('Real') return Solver.__encodeTerminal(symbolicExpression, type), type elif isinstance(symbolicExpression, sympy.Float): type = Type('Real') return Solver.__encodeTerminal(symbolicExpression, type), type else: type = Type('Real') return Solver.__encodeTerminal(symbolicExpression, type), type
def bv2pysmt(bv): """Convert a bit-vector type to a pySMT type. >>> from arxpy.bitvector.core import Constant, Variable >>> from arxpy.diffcrypt.smt import bv2pysmt >>> bv2pysmt(Constant(0b00000001, 8)) 1_8 >>> x, y = Variable("x", 8), Variable("y", 8) >>> bv2pysmt(x) x >>> bv2pysmt(x + y) (x + y) >>> bv2pysmt(x <= y) (x u<= y) >>> bv2pysmt(x[4: 2]) x[2:4] """ msg = "unknown conversion of {} to a pySMT type".format(type(bv).__name__) if isinstance(bv, int): return bv if isinstance(bv, core.Variable): return sc.Symbol(bv.name, typing.BVType(bv.width)) if isinstance(bv, core.Constant): return sc.BV(bv.val, bv.width) if isinstance(bv, operation.Operation): args = [bv2pysmt(a) for a in bv.args] if type(bv) == operation.BvNot: if args[0].is_equals(): return sc.Not(*args) else: return sc.BVNot(*args) if type(bv) == operation.BvAnd: return sc.BVAnd(*args) if type(bv) == operation.BvOr: return sc.BVOr(*args) if type(bv) == operation.BvXor: return sc.BVXor(*args) if type(bv) == operation.BvComp: # return sc.BVComp(*args) return sc.Equals(*args) if type(bv) == operation.BvUlt: return sc.BVULT(*args) if type(bv) == operation.BvUle: return sc.BVULE(*args) if type(bv) == operation.BvUgt: return sc.BVUGT(*args) if type(bv) == operation.BvUge: return sc.BVUGE(*args) if type(bv) == operation.BvShl: # Left hand side width must be a power of 2 if (args[0].bv_width() & (args[0].bv_width() - 1)) == 0: return sc.BVLShl(*args) else: x, r = bv.args offset = 0 while (x.width & (x.width - 1)) != 0: x = operation.ZeroExtend(x, 1) r = operation.ZeroExtend(r, 1) offset += 1 shift = bv2pysmt(x << r) return sc.BVExtract(shift, end=shift.bv_width() - offset - 1) # width = args[0].bv_width() # assert (width & (width - 1)) == 0 # power of 2 # return sc.BVLShl(*args) if type(bv) == operation.BvLshr: # Left hand side width must be a power of 2 if (args[0].bv_width() & (args[0].bv_width() - 1)) == 0: return sc.BVLShr(*args) else: x, r = bv.args offset = 0 while (x.width & (x.width - 1)) != 0: x = operation.ZeroExtend(x, 1) r = operation.ZeroExtend(r, 1) offset += 1 shift = bv2pysmt(x >> r) return sc.BVExtract(shift, end=shift.bv_width() - offset - 1) # width = args[1].bv_width() # assert (width & (width - 1)) == 0 # power of 2 # return sc.BVLShr(*args) if type(bv) == operation.RotateLeft: # Left hand side width must be a power of 2 if (args[0].bv_width() & (args[0].bv_width() - 1)) == 0: return sc.BVRol(*args) else: x, r = bv.args n = x.width return bv2pysmt(operation.Concat(x[n - r - 1:], x[n - 1:n - r])) if type(bv) == operation.RotateRight: # Left hand side width must be a power of 2 if (args[0].bv_width() & (args[0].bv_width() - 1)) == 0: return sc.BVRor(*args) else: x, r = bv.args n = x.width return bv2pysmt(operation.Concat(x[r - 1:], x[n - 1:r])) if type(bv) == operation.Ite: if args[0].is_equals(): a0 = args[0] else: a0 = sc.Equals(args[0], bv2pysmt(core.Constant(1, 1))) return sc.Ite(a0, *args[1:]) if type(bv) == operation.Extract: return sc.BVExtract(args[0], args[2], args[1]) if type(bv) == operation.Concat: return sc.BVConcat(*args) if type(bv) == operation.ZeroExtend: return sc.BVZExt(*args) if type(bv) == operation.Repeat: return args[0].BVRepeat(args[1]) if type(bv) == operation.BvNeg: return sc.BVNeg(*args) if type(bv) == operation.BvAdd: return sc.BVAdd(*args) if type(bv) == operation.BvSub: return sc.BVSub(*args) if type(bv) == operation.BvMul: return sc.BVMul(*args) if type(bv) == operation.BvMul: return sc.BVMul(*args) if type(bv) == operation.BvUdiv: return sc.BVUDiv(*args) if type(bv) == operation.BvUrem: return sc.BVURem(*args) raise NotImplementedError(msg)
def initialize(self, mdp, colors, hole_options, reward_name, okay_states, target_states, threshold, relation): logger.warning("This approach has been tested sparsely.") prob0E, prob1A = stormpy.compute_prob01min_states( mdp, okay_states, target_states) sink_states = ~okay_states assert len(mdp.initial_states) == 1 self.state_vars = [ smt.Symbol("p_{}".format(i), smt.REAL) for i in range(mdp.nr_states) ] self.state_prob1_vars = [ smt.Symbol("asure_{}".format(i)) for i in range(mdp.nr_states) ] self.state_probpos_vars = [ smt.Symbol("x_{}".format(i)) for i in range(mdp.nr_states) ] self.state_order_vars = [ smt.Symbol("r_{}".format(i), smt.REAL) for i in range(mdp.nr_states) ] self.option_vars = dict() for h, opts in hole_options.items(): self.option_vars[h] = { index: smt.Symbol("h_{}_{}".format(h, opt)) for index, opt in enumerate(opts) } self.transition_system = [] logger.debug("Obtain rewards if necessary") rewards = mdp.reward_models[reward_name] if reward_name else None if rewards: assert not rewards.has_transition_rewards state_rewards = rewards.has_state_rewards action_rewards = rewards.has_state_action_rewards logger.debug( "Model has state rewards: {}, has state/action rewards {}". format(state_rewards, action_rewards)) self.transition_system.append( self.state_prob1_vars[mdp.initial_states[0]]) threshold_inequality, action_constraint_inequality = self._to_smt_relation( relation) # TODO or GE self.transition_system.append( threshold_inequality(self.state_vars[mdp.initial_states[0]], smt.Real(float(threshold)))) state_order_domain_constraint = smt.And([ smt.And(smt.GE(var, smt.Real(0)), smt.LE(var, smt.Real(1))) for var in self.state_order_vars ]) self.transition_system.append(state_order_domain_constraint) #TODO how to ensure that prob is zero if there is no path. select_parameter_value_constraints = [] for h, opts in self.option_vars.items(): select_parameter_value_constraints.append( smt.Or(ov for ov in opts.values())) for i, opt1 in enumerate(opts.values()): for opt2 in list(opts.values())[i + 1:]: select_parameter_value_constraints.append( smt.Not(smt.And(opt1, opt2))) #logger.debug("Consistency: {}".format(smt.And(select_parameter_value_constraints))) self.transition_system.append( smt.And(select_parameter_value_constraints)) for state in mdp.states: if sink_states.get(state.id): assert rewards is None self.transition_system.append( smt.Equals(self.state_vars[state.id], smt.REAL(0))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) self.transition_system.append( smt.Not(self.state_prob1_vars[state.id])) #logger.debug("Constraint: {}".format(self.transition_system[-1])) self.transition_system.append( smt.Equals(self.state_order_vars[state.id], smt.Real(0))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) elif target_states.get(state.id): self.transition_system.append( smt.Equals(self.state_order_vars[state.id], smt.Real(1))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) self.transition_system.append(self.state_prob1_vars[state.id]) #logger.debug("Constraint: {}".format(self.transition_system[-1])) if rewards is None: self.transition_system.append( smt.Equals(self.state_vars[state.id], smt.Real(1))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) else: self.transition_system.append( self.state_probpos_vars[state.id]) #logger.debug("Constraint: {}".format(self.transition_system[-1])) self.transition_system.append( smt.Equals(self.state_vars[state.id], smt.Real(0))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) else: state_in_prob1A = False state_in_prob0E = False if prob0E.get(state.id): state_in_prob0E = True else: self.transition_system.append( smt.Equals(self.state_order_vars[state.id], smt.Real(1))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) self.transition_system.append( self.state_probpos_vars[state.id]) #logger.debug("Constraint: {}".format(self.transition_system[-1])) if rewards and not state_in_prob0E: if prob1A.get(state.id): self.transition_system.append( self.state_prob1_vars[state.id]) logger.debug("Constraint: {}".format( self.transition_system[-1])) state_in_prob1A = True for action in state.actions: action_index = mdp.nondeterministic_choice_indices[ state.id] + action.id #logger.debug("Action index: {}".format(action_index)) precondition = smt.And([ self.option_vars[hole][list(option)[0]] for hole, option in colors.get(action_index, dict()).items() ]) reward_value = None if rewards: reward_const = (rewards.get_state_reward( state.id) if state_rewards else 0.0) + ( rewards.get_state_action_reward(action_index) if action_rewards else 0.0) reward_value = smt.Real(reward_const) act_constraint = action_constraint_inequality( self.state_vars[state.id], smt.Plus([ smt.Times(smt.Real(t.value()), self. state_vars[t.column]) for t in action.transitions ] + [reward_value] if reward_value else [])) full_act_constraint = act_constraint if state_in_prob0E: if not rewards: full_act_constraint = smt.And( smt.Implies(self.state_probpos_vars[state.id], act_constraint), smt.Implies( smt.Not(self.state_probpos_vars), smt.Equals(self.state_vars[state.id], smt.Real(0)))) self.transition_system.append( smt.Implies( precondition, smt.Iff( self.state_probpos_vars[state.id], smt.Or([ smt.And( self.state_probpos_vars[t.column], smt.LT( self.state_order_vars[ state.id], self.state_order_vars[ t.column])) for t in action.transitions ])))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) if rewards and not state_in_prob1A: # prob_one(state) <-> probpos AND for all succ prob_one(succ) self.transition_system.append( smt.Implies( precondition, smt.Iff( self.state_prob1_vars[state.id], smt.And([ self.state_prob1_vars[t.column] for t in action.transitions ] + [self.state_probpos_vars[state.id]])))) #logger.debug("Constraint: {}".format(self.transition_system[-1])) self.transition_system.append( smt.Implies(precondition, full_act_constraint)) #logger.debug("Constraint: {}".format(self.transition_system[-1])) if rewards: self.transition_system.append( smt.And([smt.GE(sv, smt.Real(0)) for sv in self.state_vars])) else: self.transition_system.append( smt.And([ smt.And(smt.GE(sv, smt.Real(0)), smt.LE(sv, smt.Real(1))) for sv in self.state_vars ])) #print(self.transition_system) formula = smt.And(self.transition_system) logger.info("Start SMT solver") model = smt.get_model(formula) if model: logger.info("SAT: Found {}".format(model)) else: logger.info("UNSAT.")