Example #1
0
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
Example #2
0
 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)]
Example #3
0
    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
Example #4
0
    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
Example #5
0
    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
Example #6
0
 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]
Example #7
0
    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)))
Example #9
0
 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,
         )
Example #10
0
    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)
Example #11
0
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]))
Example #12
0
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)
Example #13
0
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 []
Example #14
0
    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
Example #15
0
    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)
Example #16
0
    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)
Example #17
0
    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)
Example #18
0
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
Example #19
0
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)
Example #20
0
 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(
     )
Example #21
0
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]
Example #22
0
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
Example #23
0
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)
Example #24
0
    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)
Example #25
0
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())
Example #26
0
    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))
Example #27
0
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
Example #28
0
    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)
Example #30
0
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))