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 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_to_smt(self, operator): operator = operator.to_canonical() # FIXME Integer rounding only applicable if x >= 0 def to_symbol(s): return smt.Symbol(s, typename=smt.types.INT) import math items = [smt.Times(smt.Int(int(math.floor(v))), to_symbol(k)) for k, v in operator.lhs.items()] lhs = smt.Plus([smt.Int(0)] + items) rhs = smt.Int(int(math.floor(operator.rhs))) assert operator.symbol == "<=" return smt.LE(lhs, rhs)
def generate_half_space(domain, real_count): coefficients = [smt.Real(random.random() * 2 - 1) * domain.get_symbol(domain.real_vars[i]) for i in range(real_count)] return smt.LE(smt.Plus(*coefficients), smt.Real(random.random() * 2 - 1))
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 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.")