def collect_conflicts(self): conflicts = [] inequalities = list( BoundsWalker(True).walk_smt(self.support) | BoundsWalker(True).walk_smt(self.weight)) for i in range(len(inequalities) - 1): for j in range(i + 1, len(inequalities)): if (inequalities[i].get_free_variables() == inequalities[j].get_free_variables()): if implies(inequalities[i], inequalities[j]): conflicts.append( smt.Implies(inequalities[i], inequalities[j])) logger.debug("%s => %s", inequalities[i], inequalities[j]) if implies(~inequalities[i], inequalities[j]): conflicts.append( smt.Implies(~inequalities[i], inequalities[j])) logger.debug("%s => %s", ~inequalities[i], inequalities[j]) if implies(inequalities[j], inequalities[i]): conflicts.append( smt.Implies(inequalities[j], inequalities[i])) logger.debug("%s => %s", inequalities[j], inequalities[i]) if implies(~inequalities[j], inequalities[i]): conflicts.append( smt.Implies(~inequalities[j], inequalities[i])) logger.debug("%s => %s", ~inequalities[j], inequalities[i]) return conflicts
def walk_smt(self, formula): """ Walks the given SMT formula (recursively visits the elements in the SMT formula-DAG) :type formula: pysmt.fnode.FNode """ if formula.is_and(): return self.walk_and(formula.args()) if formula.is_or(): return self.walk_or(formula.args()) if formula.node_type() == IMPLIES: return self.walk_implies(formula.arg(0), formula.arg(1)) if formula.is_not(): return self.walk_not(formula.arg(0)) if formula.is_times(): return self.walk_times(formula.args()) if formula.is_plus(): return self.walk_plus(formula.args()) if formula.is_minus(): return self.walk_minus(formula.arg(0), formula.arg(1)) if formula.is_ite(): return self.walk_ite(formula.arg(0), formula.arg(1), formula.arg(2)) if formula.node_type() == POW: return self.walk_pow(formula.arg(0), formula.arg(1)) if formula.is_le(): return self.walk_lte(formula.arg(0), formula.arg(1)) if formula.is_lt(): return self.walk_lt(formula.arg(0), formula.arg(1)) if formula.is_equals(): return self.walk_equals(formula.arg(0), formula.arg(1)) if formula.is_symbol(): return self.walk_symbol(formula.symbol_name(), formula.symbol_type()) if formula.is_constant(): return self.walk_constant(formula.constant_value(), formula.constant_type()) if formula.is_iff(): return self.walk_and([ smt.Implies(formula.arg(0), formula.arg(1)), smt.Implies(formula.arg(1), formula.arg(0)), ]) raise RuntimeError("Cannot walk {} (of type {})".format( formula, formula.node_type()))
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
def smt_to_nested(expression): # type: (FNode) -> str """ Converts an smt expression to a nested formula :param expression: An SMT expression (FNode) :return: A string representation (lisp-style) Functional operators can be: &, |, *, +, <=, <, ^ Variables are represented as (var type name) Constants are represented as (const type name) Types can be: real, int, bool """ def convert_children(op): return "({} {})".format( op, " ".join(smt_to_nested(arg) for arg in expression.args())) def format_type(smt_type): if smt_type == smt.REAL: return "real" if smt_type == smt.INT: return "int" if smt_type == smt.BOOL: return "bool" raise RuntimeError("No type corresponding to {}".format(smt_type)) if expression.is_and(): return convert_children("&") if expression.is_or(): return convert_children("|") if expression.node_type() == IMPLIES: return smt_to_nested( smt.Or(smt.Not(expression.args()[0]), expression.args()[1])) if expression.is_iff(): return smt_to_nested( smt.And(smt.Implies(expression.args()[0], expression.args()[1]), smt.Implies(expression.args()[1], expression.args()[0]))) if expression.is_not(): return convert_children("~") if expression.is_times(): return convert_children("*") if expression.is_plus(): return convert_children("+") if expression.is_minus(): return convert_children("-") if expression.is_ite(): return convert_children("ite") if expression.node_type() == POW: exponent = expression.args()[1] if exponent.is_constant() and exponent.constant_value() == 1: return smt_to_nested(expression.args()[0]) else: return convert_children("^") if expression.is_le(): return convert_children("<=") if expression.is_lt(): return convert_children("<") if expression.is_equals(): return convert_children("=") if expression.is_symbol(): return "(var {} {})".format(format_type(expression.symbol_type()), expression.symbol_name()) if expression.is_constant(): value = expression.constant_value() if isinstance(value, fractions.Fraction): value = float(value) return "(const {} {})".format(format_type(expression.constant_type()), value) raise RuntimeError("Cannot convert {} (of type {})".format( expression, expression.node_type()))
def to_smt(self): return sc.Implies(self._folA.to_smt(), self._folB.to_smt())
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.")