def get_coefficients(atom: FNode): """ obtain coefficient of variable x in atom of form a * x + b * y + const note that when there is a * x + b * x, simplify() doesn't do multiplication but still here we return (a + b) :param atom: FNode, formula :param x: FNode, symbol of variable :return: dict with keys as variable symbol and values as coefficient in atom """ variables = list(get_real_variables(atom)) coefficients = defaultdict(int) if len(variables) == 0: return coefficients const = get_constants(atom) atom = simplify(Plus(atom, -const)) sub_dict = dict().fromkeys(variables, Real(0)) for i in range(len(variables)): sub_dict[variables[i]] = Real(1) coefficient = simplify(atom.substitute(sub_dict)) coefficients[variables[i]] = coefficient sub_dict[variables[i]] = Real(0) return coefficients
def two_comparator(input1, input2): if SortingNetwork.simplify: return [ simplify(Or(input1, input2)), simplify(And(input1, input2)) ] else: return [Or(input1, input2), And(input1, input2)]
def get_upper_bound_expectation_dnf(self, upper_bound_expectation, ignore_conjuncts_with_infinity = True): """ Takes a candidate upper bound expectation (as a String) and returns its DNF, i.e. a list of the form [(guard_1, arith_1, ..., guard_n, arith_n)]. If ignore_conjuncts_with_infinity = False, then the following holds (1) for all i != j, the formula guard_i AND guard_j is UNSAT, and (2) guard_1 OR ... OR guard_n is equivalent to TRUE. :param upper_bound_expectation: The candidate upper bound that is to be checked. :param ignore_conjuncts_with_infinity: For the refute-queries, conjuncts with arith_exp = infinity can be ignored since nothing is greater than infinity. """ probably_upper_bound_expectation = parse_expectation(upper_bound_expectation) if type(probably_upper_bound_expectation) == CheckFail: print(probably_upper_bound_expectation) raise Exception("CheckFail.") self.is_linear = self.is_linear and check_is_linear_expr(probably_upper_bound_expectation) == None flattened_upper_bound_expectation = normalize_expectation_simple(self.program, probably_upper_bound_expectation) logger.info( "Flattened Upper Bound Expectation: %s" % flattened_upper_bound_expectation) pysmt_upper_bound_snf = [(probably_expr_to_pysmt(bool_exp, self._pysmt_infinity_variable, True, self.monus_euf, False), probably_expr_to_pysmt(arith_exp, self._pysmt_infinity_variable, True, self.rmonus_euf, True)) for bool_exp, arith_exp in flattened_upper_bound_expectation] pysmt_upper_bound_dnf = [] for bin_seq in product([True, False], repeat=len(pysmt_upper_bound_snf)): guard_seq, arith_seq = zip( *list(map(self._construct_boolexp_arithexp_par, bin_seq, pysmt_upper_bound_snf))) to_test = And(guard_seq + (self.non_negative_constraint,)) if self._sat_solver.is_sat(to_test): # If arith_seq contains infinity, then the whole arithmetic expression is to be interpreted as infinity # Since nothing is greater than infinity, states satisfying And(guard_seq) are always an upper bound and can thus be omitted. if not self._pysmt_infinity_variable in arith_seq or not ignore_conjuncts_with_infinity: resulting_arith = simplify(Plus(arith_seq)) pysmt_upper_bound_dnf.append( (simplify(And(guard_seq)), resulting_arith)) logger.info("Upper Bound DNF (ignore_conjuncts_with_infinity = %s): %s" % (ignore_conjuncts_with_infinity, ["(%s,%s)" % (guard.serialize(), arith.serialize()) for (guard, arith) in pysmt_upper_bound_dnf])) if not ignore_conjuncts_with_infinity: logger.debug("Checking whether the Boolean expressions occurring in the DNF partition the state space if all variables are non-negative.") # The k-induction encoding is not sound if this does not hold. assert(not self._sat_solver.is_sat(And([self.non_negative_constraint, Not(Or([guard for (guard, arith) in pysmt_upper_bound_dnf]))]))) return pysmt_upper_bound_dnf
def _get_pysmt_loop_terminated_dnf(self, probably_summation_nf: SnfLoopExpectationTransformer, post_expectation): """ Computes the loop-terminated-part of the wp-characteristic functional in PySMT disjunctive normal form. :param initialization: The parsed variable declarations of the program. :param probably_summation_nf: The probably summation normal form of the wp-characteristic functional. :param post_expectation: The postexpectation that is to be parsed and processed. :return: The PySMT dnf of the execute-loop-part. """ logger.info(probably_summation_nf.done) pysmt_loop_done = probably_expr_to_pysmt(probably_summation_nf.done, self._pysmt_infinity_variable, True, self.monus_euf) probably_postexpectation = parse_expectation(post_expectation) self.is_linear = self.is_linear and check_is_linear_expr(probably_postexpectation)==None flattened_postexpectation = normalize_expectation_simple(self.program, probably_postexpectation) if type(flattened_postexpectation) == CheckFail: print(flattened_postexpectation) raise Exception("CheckFail.") logger.info("Flattened Postexpectation: %s \n Loop Done Expression: %s" % (flattened_postexpectation, pysmt_loop_done)) pysmt_postexpectation_snf = [(probably_expr_to_pysmt(bool_exp, self._pysmt_infinity_variable, True, self.monus_euf), probably_expr_to_pysmt(arith_exp, self._pysmt_infinity_variable, True, self.rmonus_euf, True)) for bool_exp, arith_exp in flattened_postexpectation] pysmt_loop_terminated_dnf = [] for bin_seq in product([True, False], repeat=len(pysmt_postexpectation_snf)): guard_seq, arith_seq = zip( *list(map(self._construct_boolexp_arithexp_par, bin_seq, pysmt_postexpectation_snf))) to_test = And(guard_seq + (pysmt_loop_done, self.non_negative_constraint,)) if self._sat_solver.is_sat(to_test): resulting_arith = simplify(Plus(arith_seq)) pysmt_loop_terminated_dnf.append( (simplify(And(guard_seq + (pysmt_loop_done,))), resulting_arith)) self._pysmt_loop_done = pysmt_loop_done logger.info("PySMT Loop-Terminated DNF: \n %s" % (pysmt_loop_terminated_dnf)) logger.debug("Checking that the conjunction of all guards is equivalent to True") return pysmt_loop_terminated_dnf
def to_canonical(self, child_true: int, child_false: int) -> Tuple['Decision', int, int]: if self.is_canonical: return self, child_true, child_false if self.is_bool(): if self.test.is_symbol(): self.is_canonical = True return self, child_true, child_false else: decision = Decision(simplify(~self.test), self._variables, True) print(decision) assert decision.test.is_symbol() return decision, child_false, child_true inequality = LinearInequality.from_smt(self.test) keys = sorted(inequality.inequality_dict.keys()) if len(keys) == 0: print("IN", inequality) factor = abs(inequality.inequality_dict[keys[0]]) result = {k: v / factor for k, v in inequality.inequality_dict.items()} if result[keys[0]] < 0: result = {k: -v for k, v in result.items()} child_true, child_false = child_false, child_true canonical_inequality = LinearInequality(result) decision = Decision(canonical_inequality.to_smt(), self._variables, True, canonical_inequality) # canonical_inequality = LinearInequality.from_smt(self.test).normalize() # if canonical_inequality.b() < 0: # canonical_inequality = LinearInequality({k: -v for k, v, in canonical_inequality.inequality_dict.items()}) # child_true, child_false = child_false, child_true # decision = Decision(canonical_inequality.to_smt(), self._variables, True, canonical_inequality) return decision, child_true, child_false
def get_valid_branches(self) -> List[bool]: """ Returns the valid branches (True, False or both) """ simplified = self.test if simplified not in [TRUE(), FALSE()]: if self.is_bool(): simplified = simplify(simplified) else: simplified = simplify(self.inequality.to_smt()) if simplified == TRUE(): return [True] elif simplified == FALSE(): return [False] else: return [True, False]
def _get_param_assignments(self, model, time, parameters, monotonic=True): p_ass = [] fwd = False for p in parameters: p_time = model[TS.get_ptimed(p, 0)] if p.symbol_type() == BOOL: if monotonic: if p_time == TRUE(): p_ass.append(p) else: p_ass.append(p if p_time == TRUE() else Not(p)) else: p_ass.append(EqualsOrIff(p, p_time)) p_ass = And(p_ass) self.region = simplify(Or(self.region, p_ass)) if self.models is None: self.models = [] self.models.append((model, time)) Logger.msg("+", 0, not (Logger.level(1))) self.cs_count += 1 Logger.log( "Found assignment \"%s\"" % (p_ass.serialize(threshold=100)), 1) return (p_ass, False)
def operator_to_bound(self, inequality: LinearInequality, var_name: str): result = Real(inequality.b()) for other in inequality.variables: if other != var_name: result += Symbol(other, REAL) * Real(-inequality.coefficient(other)) return simplify(result * Real(1 / inequality.coefficient(var_name)))
def walk_literal(self, l, node): vertex_id = self.get_id(node.id) if self.literals.is_number_boolean(l): label = "[{}] {}".format(node.id, ("~" if l < 0 else "") + str(self.literals.number_to_val(l))) if node.id in self.node_annotations: label += ": {}".format(self.node_annotations[node.id]) return ( vertex_id, { '{} [label="{}",color=black,shape=rectangle];'.format( vertex_id, label) }, set(), node.id, ) else: inequality = self.literals.number_to_val(l) if l < 0: inequality = smt.simplify(~inequality) label = "[{}] {}".format(node.id, str(inequality)) if node.id in self.node_annotations: label += ": {}".format(self.node_annotations[node.id]) return ( vertex_id, { '{} [label="{}",color=black,shape=rectangle];'.format( vertex_id, label) }, set(), node.id, )
def _get_param_assignments(self, model, time, parameters, monotonic=True): p_ass = [] fwd = False for p in parameters: # search the trace for any enabled faults ever_true = False for t in range(time + 1): p_time = model[TS.get_ptimed(p, 0)] if p.symbol_type() == BOOL: if p_time == TRUE(): ever_true = True break if ever_true: p_ass.append(p) elif not monotonic: p_ass.append(EqualsOrIff(p, FALSE())) p_ass = And(p_ass) self.region = simplify(Or(self.region, p_ass)) if self.models is None: self.models = [] self.models.append((model, time)) Logger.msg("+", 0, not (Logger.level(1))) self.cs_count += 1 Logger.log( "Found assignment \"%s\"" % (p_ass.serialize(threshold=100)), 1) return (p_ass, False)
def categorize_bounds(formula: FNode, sender_symbol: FNode): """ categorize symbolic bounds for a given variable :param formula: FNode formula in CNF form :param sender_symbol: FNode of the bounded variable :return: upper_bounds, lower_bounds, parent_atoms """ clauses = set() formula = simplify(formula) if formula.is_or() or is_literal(formula): clauses.add(formula) else: # formula of CNF form for clause in formula.args(): assert is_literal(clause) or clause.is_or() clauses.add(clause) bounds = defaultdict(list) for clause in clauses: if clause.is_le() or clause.is_lt(): # single literal clause bound, bound_type = atom_to_bound(clause, sender_symbol) bounds[bound_type].append(bound) continue for atom in clause.get_atoms(): # atom: inequality a * x + b * y < c assert atom.is_le() or atom.is_lt() bound, bound_type = atom_to_bound(atom, sender_symbol) bounds[bound_type].append(bound) return list(set(bounds[UPPER])), list(set(bounds[LOWER])), \ list(set(bounds[NEITHER]))
def validateModel(smtFile, modelFile): try: if (not path.exists(smtFile)): raise Exception("File not found: {}".format(smtFile)) if (not path.exists(modelFile)): raise Exception("File not found: {}".format(modelFile)) parser = SmtLibParser() formula = readSmtFile(parser, smtFile) symbols = getSymbols(parser) model = readModel(parser, modelFile) checkFullModel(model, symbols) result = simplify(formula.substitute(model)) if (not result.is_constant()): print("INVALID: Did not provide a full model.") elif (not result.is_true()): print("INVALID: Model does not evaluate to true.") else: print("VALID") except Exception as e: print(e) sys.exit(1)
def clause_to_intervals(clause: FNode, sender_symbol: FNode, test_point: float): """ turn clause into a list of intervals, where there are at most two intervals :param clause: OR FNode or a single atom :param sender_symbol: :param test_point: an initiation value for recipient variable :return: list of tuples of form [lower, upper] """ upper_bounds, lower_bounds, recipient_atoms = categorize_bounds( clause, sender_symbol) if recipient_atoms: parent_symbol = list(get_real_variables(recipient_atoms[0])) for atom in recipient_atoms: bound = simplify( substitute(atom, {parent_symbol[0]: Real(test_point)})) if bound.is_true(): return [] num_uppers = [initiate_bound(upper, test_point) for upper in upper_bounds] num_lowers = [initiate_bound(lower, test_point) for lower in lower_bounds] if not num_uppers and not num_lowers: return [] if not num_uppers: return [[min(num_lowers), float('inf')]] if not num_lowers: return [[float('-inf'), max(num_uppers)]] max_upper, min_lower = max(num_uppers), min(num_lowers) if max_upper < min_lower: return [[float('-inf'), max_upper], [min_lower, float('inf')]] else: return []
def _get_pysmt_dnf_loop_execute(self, pysmt_summation_nf): """ Computes the execute-loop-part of the wp-characteristic functional in PySMT disjunctive normal form :param pysmt_summation_nf: The wp-characteristic functional in summation normal form. :return: The PySMT dnf of the execute-loop-part. """ pysmt_dnf_loop_execute = [] for bin_seq in product([True, False], repeat=len(pysmt_summation_nf)): guard_seq, prob_seq, sub_seq, tick_seq = zip(*list(map(self._construct_guard_prob_tick_triple, bin_seq, pysmt_summation_nf))) conjuncted_B = And(guard_seq) to_test = And(guard_seq + (self.non_negative_constraint,)) # We only want to add them, if they are satisfiable if self._sat_solver.is_sat(to_test): prob_sub_tick_list = [(prob_seq[i], sub_seq[i], tick_seq[i]) for i in range(0, len(prob_seq)) if not prob_seq[i] == Real(0)] # if the prob_sub_list is empty, then this entry of the dnf corresponds to the (not guard) part of # the wp-characteristic functional. Omit this part in the loop_execute part. if len(prob_sub_tick_list) != 0: pysmt_dnf_loop_execute.append((simplify(conjuncted_B), prob_sub_tick_list)) logger.info("PySMT Disjunctive NF (length = %s): \n %s \n" % ( len(pysmt_dnf_loop_execute), [(guard.serialize(), prob_subs_ticks) for (guard, prob_subs_ticks) in pysmt_dnf_loop_execute])) return pysmt_dnf_loop_execute
def compile_ftrans(self): if self.ftrans is None: return None ret_trans = TRUE() ret_invar = TRUE() use_ites = False if use_ites: for var, cond_assign_list in self.ftrans.items(): if TS.has_next(var): ite_list = TS.to_prev(var) else: ite_list = var for (condition, value) in reversed(cond_assign_list): if condition == TRUE(): ite_list = value else: ite_list = Ite(condition, value, ite_list) if TS.has_next(ite_list): ret_trans = And(ret_trans, EqualsOrIff(var, ite_list)) else: ret_invar = And(ret_invar, EqualsOrIff(var, ite_list)) else: for var, cond_assign_list in self.ftrans.items(): effects = [] all_neg_cond = [] for (condition, value) in cond_assign_list: effects.append( simplify(Implies(condition, EqualsOrIff(var, value)))) all_neg_cond.append(Not(condition)) if not TS.has_next(var) and not TS.has_next(condition): ret_invar = And(ret_invar, And(effects)) else: ret_trans = And(ret_trans, And(effects)) if TS.has_next(var): no_change = EqualsOrIff(var, TS.to_prev(var)) ret_trans = And( ret_trans, simplify(Implies(And(all_neg_cond), no_change))) return (ret_invar, ret_trans)
def add_ts(self, ts): if self.en_simplify: ts.init = simplify(ts.init) ts.invar = simplify(ts.invar) ts.trans = simplify(ts.trans) self.tss.add(ts) for v in ts.vars: self.vars.add(v) for v in ts.state_vars: self.state_vars.add(v) for v in ts.input_vars: self.input_vars.add(v) for v in ts.output_vars: self.output_vars.add(v) self.update_logic(ts.logic)
def add_ts(self, ts, add_vars=True, reset=True): if self.en_simplify: ts.init = simplify(ts.init) ts.invar = simplify(ts.invar) ts.trans = simplify(ts.trans) self.tss.add(ts) if add_vars: for v in ts.vars: self.vars.add(v) if (v in ts.state_vars) and (v not in self.input_vars): self.state_vars.add(v) if v in ts.input_vars: self.input_vars.add(v) if (v in ts.output_vars) and (v not in self.input_vars): self.output_vars.add(v) self.update_logic(ts.logic) if reset: init = True trans = True invar = True ftrans = True if (ts.init is None) or (ts.init == TRUE()): init = False if (ts.invar is None) or (ts.invar == TRUE()): invar = False if (ts.trans is None) or (ts.trans == TRUE()): trans = False if (ts.ftrans is None) or (ts.ftrans == {}): ftrans = False self.reset_formulae(init=init, invar=invar, trans=trans, ftrans=ftrans)
def literal_to_bounds(f: FNode): """ obtain bounds on variable x from inequality atom :param literal: ax + by + c < dx + ey + f :param x: variable :return: symbolic bound for x, is_lower, k_inclduded for example, given literal 3x + 2y + 4 < x + y + 1, first move all terms to left hand side: 2x + y + 3 < 0, then it returns [-1/2 y - 3/2, False] since the literal is equivalent to x < -1/2 y - 3/2 """ assert (is_literal(f)) assert f.is_le() or f.is_lt() variables = list(get_real_variables(f)) k_included = f.is_le() f = simplify(f) lhs, rhs = f.arg(0), f.arg(1) lhs_coef, lhs_const = get_coefficients(lhs), get_constants(lhs) rhs_coef, rhs_const = get_coefficients(rhs), get_constants(rhs) # move all terms to lhs const = simplify(lhs_const - rhs_const) coef = dict() for v in variables: coef[v] = simplify(lhs_coef[v] - rhs_coef[v]) bounds = dict() for v in variables: if float(coef[v].constant_value()) == 0: continue bound_terms = [simplify(const * Real(-1) / coef[v])] for w in variables: if w == v or float(coef[w].constant_value()) == 0: continue new_w_coef = simplify(coef[w] * Real(-1) / coef[v]) bound_terms.append(Times(new_w_coef, w)) bound = Plus(bound_terms) is_lower = coef[v].constant_value() < 0 bounds[v] = bound, is_lower, k_included return bounds
def get_constants(atom: FNode): """ obtain constant term in a linear real arithmetics :param atom: FNode formula of form a * x + b * y + c :return: """ variables = list(get_real_variables(atom)) new_atom = atom for var in variables: new_atom = new_atom.substitute({var: Real(0)}) return simplify(new_atom)
def __init__(self, parent, child, formula): """ :param formula: clause must be of form that child variable on one side and other variables and constant on the other side, e.g. root+c <= child :param child: TNode object """ self.parent = parent self.child = child self.formula = simplify(formula) self.check_formula() self.upper_bounds, self.lower_bounds, self.critical_points = self.collect_bounds( )
def atom_to_bound(atom: FNode, x_symbol: FNode): """ obtain bounds on variable x from inequality atom :param atom: must be of plus form or a single term on each side :param x_symbol: :return: """ assert atom.is_le() or atom.is_lt() variables = list(get_real_variables(atom)) if x_symbol not in variables: return [atom, NEITHER] lhs, rhs = atom.arg(0), atom.arg(1) lhs_coef, lhs_const = get_coefficients(lhs), get_constants(lhs) rhs_coef, rhs_const = get_coefficients(rhs), get_constants(rhs) lhs_x_coef = lhs_coef[x_symbol] if lhs_coef.get(x_symbol) else Real(0) rhs_x_coef = rhs_coef[x_symbol] if rhs_coef.get(x_symbol) else Real(0) x_coef_diff = simplify(lhs_x_coef - rhs_x_coef) assert float(x_coef_diff.constant_value()) != 0 flag = simplify(x_coef_diff > 0) bound = simplify((rhs_const - lhs_const) / x_coef_diff) bound_type = UPPER if flag.is_true() else LOWER if len(variables) == 1: return [bound, bound_type] y_symbol = variables[0] if x_symbol == variables[1] else variables[1] lhs_y_coef = lhs_coef[y_symbol] if lhs_coef.get(y_symbol) else Real(0) rhs_y_coef = rhs_coef[y_symbol] if rhs_coef.get(y_symbol) else Real(0) y_coef_diff = simplify(lhs_y_coef - rhs_y_coef) if float(y_coef_diff.constant_value()) == 0: return [bound, bound_type] new_y_coef = simplify(y_coef_diff * Real(-1) / x_coef_diff) bound = Plus(bound, Times(new_y_coef, y_symbol)) return [bound, bound_type]
def parse_univariate_literal(f): """ Not complete. Given a univariate literal f, returns: - the variable x - the simplified endpoint k - a flag that is True iff f is a lower bound to x - a flag that is True iff the endpoint k is included WARNING: again, not complete, i.e. it doesn't work for arbitrary univariate literals. """ assert (is_literal(f)) assert (len(f.get_free_variables()) == 1) # TODO: is this needed, given that we flip every xRA literal as a # preprocessing step? if f.is_not(): f = flip_ra_atom(f.args()[0]) f = simplify(f) x = list(f.get_free_variables())[0] k_included = f.is_le() l, r = f.args() if l.is_real_constant(): is_lower = True k = l.constant_value() ax = r elif r.is_real_constant(): is_lower = False k = r.constant_value() ax = l else: raise ValueError(f"Can't parse the univariate literal {f.serialize()}") if ax.is_times(): assert (len(ax.args()) == 2) assert (ax.args()[0].is_symbol()) assert (ax.args()[1].is_real_constant()) k = k / ax.args()[1].constant_value() elif ax.is_plus(): assert (len(ax.args()) == 2) assert (ax.args()[0].is_symbol()) assert (ax.args()[1].is_real_constant()) k = k - ax.args()[1].constant_value() elif not ax.is_symbol(): raise ValueError(f"Can't parse the univariate literal {f.serialize()}") return x, k, is_lower, k_included
def get_x_coefficient(atom: FNode): """ obtain coefficient of variable in univariate LRA :param atom: FNode atom :return: coeffcient of variable, type FNode """ variable = list(get_real_variables(atom)) assert len(variable) == 1 var = variable[0] const = get_constants(atom) new_atom = Plus(atom, -const) new_atom = new_atom.substitute({var: Real(1)}) return simplify(new_atom)
def __init__(self, value=SMYBOLIC, *, name=AUTOMATIC, prefix=AUTOMATIC): if (name is not AUTOMATIC or prefix is not AUTOMATIC) and value is not SMYBOLIC: raise TypeError('Can only name symbolic variables') elif name is not AUTOMATIC and prefix is not AUTOMATIC: raise ValueError('Can only set either name or prefix not both') elif name is not AUTOMATIC: if not isinstance(name, str): raise TypeError('Name must be string') elif name in _name_table: raise ValueError(f'Name {name} already in use') elif _name_re.fullmatch(name): warnings.warn( 'Name looks like an auto generated name, this might break things' ) _name_table[name] = self elif prefix is not AUTOMATIC: name = _gen_name(prefix) _name_table[name] = self elif name is AUTOMATIC and value is SMYBOLIC: name = _gen_name() _name_table[name] = self if value is SMYBOLIC: self._value = smt.Symbol(name, BOOL) elif isinstance(value, pysmt.fnode.FNode): if value.get_type().is_bool_type(): self._value = value else: raise TypeError(f'Expected bool type not {value.get_type()}') elif isinstance(value, SMTBit): if name is not AUTOMATIC and name != value.name: warnings.warn( 'Changing the name of a SMTBit does not cause a new underlying smt variable to be created' ) self._value = value._value elif isinstance(value, bool): self._value = smt.Bool(value) elif isinstance(value, int): if value not in {0, 1}: raise ValueError( 'Bit must have value 0 or 1 not {}'.format(value)) self._value = smt.Bool(bool(value)) elif hasattr(value, '__bool__'): self._value = smt.Bool(bool(value)) else: raise TypeError("Can't coerce {} to Bit".format(type(value))) self._name = name self._value = smt.simplify(self._value)
def initiate_bound(bound: FNode, initiation: float) -> float: """ initiate an atom with only one variable with value initiation :param bound: FNode atom :param initiation: initiation of variable, type float :return: initiated bound, type float """ real_variables = list(get_real_variables(bound)) assert len(real_variables) < 2 if real_variables: # print(real_variables[0], Real(initiation)) bound = simplify( substitute(bound, {real_variables[0]: Real(initiation)})) return float(bound.constant_value())
def __print_single_ts(self, ts): has_comment = len(ts.comment) > 0 if has_comment: lenstr = len(ts.comment) + 3 self.write("\n%s\n" % ("-" * lenstr)) self.write("# %s\n" % ts.comment) self.write("%s\n" % ("-" * lenstr)) sections = [("VAR", [x for x in ts.vars if x not in list(ts.state_vars)+list(ts.input_vars)+list(ts.output_vars)]),\ ("STATE", ts.state_vars),\ ("INPUT", ts.input_vars),\ ("OUTPUT", ts.output_vars)] for (sname, vars) in sections: if len(vars) > 0: self.write("%s\n" % sname) for var in vars: sname = self.names(var.symbol_name()) if var.symbol_type() == BOOL: self.write("%s : Bool;\n" % (sname)) else: self.write("%s : BV(%s);\n" % (sname, var.symbol_type().width)) self.write("\n") sections = [((ts.init), "INIT"), ((ts.invar), "INVAR"), ((ts.trans), "TRANS")] for (formula, keyword) in sections: if formula not in [TRUE(), FALSE()]: self.write("%s\n" % keyword) cp = list(conjunctive_partition(formula)) if self.simplify: cp = self._simplify_cp(cp) for i in range(len(cp)): f = simplify(cp[i]) if f == TRUE(): continue self.printer(f) self.write(";\n") if f == FALSE(): break self.write("\n") if has_comment: self.write("\n%s\n" % ("-" * lenstr))
def check_clause(formula: FNode): """ check if formula is either an atom or disjunctive of atoms :param formula: :return: """ flag = True formula = simplify(formula) if len(formula.get_atoms()) == 1: return True if not formula.is_or(): return False for atom in formula.get_atoms(): flag = flag and (atom in atom.get_atoms()) return flag
def _simplify_cp(self, cp): random.shuffle(cp) newcp = [] last = False step = 3 for i in range(0, len(cp) - (step - 1), step): if i == len(cp) - step: last = True formula = simplify(And([cp[i + j] for j in range(step)])) newcp += list(conjunctive_partition(formula)) if not last: for i in range(-1, -step, -1): newcp.append(cp[i]) return newcp
def with_evidence(self, substitutions): # type: (T, Dict[FNode, FNode]) -> T variables_to_remove = set() for k, v in substitutions.items(): assert k.is_symbol() assert k.symbol_type() == BOOL assert v.is_constant() variables_to_remove.add(k.symbol_name()) variables = [ v for v in self.domain.variables if v not in variables_to_remove ] domain = Domain(variables, {v: self.domain.var_types[v] for v in variables}, self.domain.var_domains) support = self.support.substitute(substitutions) weight = simplify(self.weight.substitute(substitutions)) return self.copy(domain, support, weight)
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))