Example #1
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 #2
0
def solve_equation(lhs: FNode,
                   rhs: FNode,
                   domains: FNode = None,
                   solver_name: str = 'msat'):
    """
    solve FNode equations
    :param lhs: FNode formula
    :param rhs: FNode formula
    :param domains: domain of variable, type FNode formula
    :param solver_name:
    :return: solution of variable, dict with key as variable and value as solution, both type FNode
    """
    problem = lhs.Equals(rhs)
    formula = problem if not domains else domains.And(problem)
    real_variables = get_real_variables(formula)

    with Solver(name=solver_name) as solver:
        solver.add_assertion(formula)
        if solver.solve():
            solution = {}
            for rvar in real_variables:
                solution[rvar] = solver.get_value(rvar)
            return solution
        else:
            return None
Example #3
0
 def cpy_tree(self, root, subs, to_sub):
     if root == None:
         return None, []
     elif root.is_symbol():
         to_sub = []
         res = FNode(
             FNodeContent(
                 root._content.node_type, (),
                 (root._content.payload[0], root._content.payload[1])),
             self.node_num)
         if root in subs:
             to_sub = [res]
         self.node_num += 1
         return res, to_sub
     else:
         childrens = []
         to_sub = []
         for node in root._content.args:
             cpy, sub = self.cpy_tree(node, subs, [])
             childrens.append(cpy)
             to_sub.extend(sub)
         node_id = self.node_num
         self.node_num += 1
         content = FNodeContent(root._content.node_type, tuple(childrens),
                                [None])
         res = FNode(content, node_id)
         return res, to_sub
Example #4
0
 def get_all_number_symbols(self):
     real = []
     int = []
     for cmd in self.script:
         if cmd.name == 'declare-fun':
             if FNode.get_type(cmd.args[0]) == INT:
                 int.append(cmd.args[0])
             if FNode.get_type(cmd.args[0]) == REAL:
                 real.append(cmd.args[0])
     return int, real
Example #5
0
def extract_labels_and_weight(weight: FNode) -> Tuple[label_dict_type, FNode]:
    labels = dict()
    terms = []
    if weight.is_times():
        for arg in weight.args():  # type: FNode
            label = get_bool_label(arg)
            if label is not None:
                labels[label[0]] = tuple(label[1:])
            else:
                terms.append(arg)
        return labels, Times(*terms)
    else:
        return labels, weight
Example #6
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
    def integrate(self, node_id: int, var: FNode) -> int:
        self.ub_cache = dict()
        self.lb_cache = dict()

        if self.reduce_strategy[0]:
            node_id = self.pool.diagram(node_id).reduce(
                method=self.method).root_id

        if logger.isEnabledFor(logging.DEBUG):
            self.pool.diagram(node_id).export_png(
                "log/integrate_{}_d_{}".format(node_id, var), pretty=True)

        if var.symbol_type() != BOOL and self.symbolic_integration_enabled:
            integrator = partial(self.symbolic_integrator, var)
            integrated = leaf_transform.transform_leaves(
                integrator, self.pool.diagram(node_id))
        else:
            integrated = node_id

        # self.export(self.pool.diagram(integrated), "integrated")
        result_id = self.resolve_lb_ub(integrated, var)
        # result_id = order.order(self.pool.diagram(self.resolve_lb_ub(integrated, var))).root_id

        self.ub_cache = None
        self.lb_cache = None

        if all(self.reduce_strategy):
            result_id = self.pool.diagram(result_id).reduce(
                method=self.method).root_id
        return result_id
Example #8
0
 def ast_contains(self, node: FNode, what: Callable[[FNode], bool]) -> bool:
     if what(node):
         return True
     for child in node.args():
         if self.ast_contains(child, what):
             return True
     return False
Example #9
0
def mul_nums_and_fnodes(nums, nodes):
    temp = []
    for index, n in enumerate(nums):
        temp.append(mul_num_and_fnode(n, nodes[index]))
    content = FNodeContent(13, tuple(temp), None)
    result = FNode(content, 300000 + nums[0])
    return result
Example #10
0
def formula_to_interval_set(formula: FNode, sender_symbol: FNode,
                            test_point: float):
    clauses = set()
    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)

    interval_list = []
    for clause in clauses:
        intervals = clause_to_intervals(clause, sender_symbol, test_point)
        if intervals:
            interval_list.append(intervals)
    return interval_list
Example #11
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 #12
0
 def create_node(self, node_type, args, payload=None):
     content = FNodeContent(node_type, args, payload)
     if content in self.formulae:
         return self.formulae[content]
     else:
         n = FNode(content)
         self.formulae[content] = n
         self._do_type_check(n)
         return n
Example #13
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 #14
0
 def create_node(self, node_type, args, payload=None):
     content = FNodeContent(node_type, args, payload, self.init_data())
     if content in self.formulae:
         return self.formulae[content]
     else:
         n = FNode(content, self._next_free_id)
         self._next_free_id += 1
         self.formulae[content] = n
         self._do_type_check(n)
         return n
 def symbolic_integrator(self, var: FNode, terminal_node: TerminalNode,
                         d: Diagram):
     algebra = self.pool.algebra
     sym = algebra.symbol(var.symbol_name())
     lb = algebra.symbol("_lb")
     ub = algebra.symbol("_ub")
     expression_bounds = algebra.times(algebra.greater_than_equal(sym, lb),
                                       algebra.less_than_equal(sym, ub))
     result = algebra.integrate(
         None, algebra.times(expression_bounds, terminal_node.expression),
         sym)
     return self.pool.terminal(result)
Example #16
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 #17
0
 def split_left_and_right(self, formula):
     L = list(formula.args())
     formula_type = formula.node_type()
     if len(L) == 2:
         L.append(formula_type)
     else:
         length = len(L)
         c = FNodeContent(formula_type, tuple(L[1:]), None)
         newNode = FNode(c, -1)
         L = L[:1]
         L.append(newNode)
         L.append(formula_type)
     return L
    def concrete_integrate(self, expression: Expression, var: FNode, lb: FNode,
                           ub: FNode, prefix) -> Expression:
        logger.debug("%s integrate %s, %s <= %s <= %s", prefix, expression, lb,
                     var, ub)
        algebra = self.pool.algebra
        lb = Polynomial.from_smt(lb).to_expression(algebra)
        ub = Polynomial.from_smt(ub).to_expression(algebra)

        var_name = var.symbol_name()
        result = algebra.integrate_poly(expression, [var_name],
                                        {var_name: (lb, ub)})
        logger.debug("%s \t          = %s", prefix, result)
        return result
Example #19
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 #20
0
def variable_inversion(script, ops):
    add_var(script, 'y', INT)
    for cmd in script:
        if cmd.name == 'declare-fun':
            continue
        for i in range(len(cmd.args)):
            to_search = [cmd.args[i]]
            if isinstance(cmd.args[i], pysmt.fnode.FNode):
                while len(to_search) != 0:
                    curr = to_search.pop(0)
                    if FNode.is_symbol(curr):
                        if curr in ops:
                            cmd.args[i] = cmd.args[i].substitute(ops)
                            break
                    for arg in curr.args():
                        to_search.append(arg)
Example #21
0
 def change_tree(self, root, sub, sub_dict):
     if root == None:
         return None
     elif root.is_symbol():
         if root._node_id in sub_dict:
             if sub_dict[root._node_id] == 1:
                 for key in sub:
                     if key.symbol_name() == root.symbol_name():
                         return sub[key]
         return root
     else:
         childrens = []
         for node in root._content.args:
             cpy = self.change_tree(node, sub, sub_dict)
             childrens.append(cpy)
         node_id = root._node_id
         content = FNodeContent(root._content.node_type, tuple(childrens),
                                [None])
         res = FNode(content, node_id)
         return res
Example #22
0
def get_const(val: FNode, match: FNode = None) -> FNode:
    '''
    Returns a bit-vector constant based on the input value.

    If match is an FNode instead of None, tries to match the bit-width
    '''
    if type(val) == FNode:
        return val
    elif type(val) == int:
        if match is not None:
            if type(match) != FNode:
                Logger.error(
                    "Expecting an FNode in get_const, but got {}".format(
                        type(match)))
            match_width = get_type(match).width
            if val.bit_length() > match_width:
                Logger.error(
                    "Trying to match bit-width of {} but can't express {} in {} bits"
                    .format(match, val, match_width))
            return BV(val, match_width)
        return BV(val, DEFAULTINT)
    else:
        raise RuntimeError("Unhandled case in get_const: {}".format(type(val)))
Example #23
0
 def super_bad_function():
     sb = FNode(FNodeContent(node_type=AND, args=(self.p, self.p),
                             payload=None), -1)
     return sb
Example #24
0
def create_new_variable(variable_index):
    variable_name = "new_variable" + str(variable_index)
    content = FNodeContent(SYMBOL, (), (variable_name, INT))
    node = FNode(content, 10000 + variable_index)
    return node
Example #25
0
def get_bool_label(formula: FNode) -> Optional[Tuple[str, FNode, FNode]]:
    if formula.is_ite():
        c, t, e = formula.args()  # type: FNode
        if c.is_symbol() and c.symbol_type() == BOOL:
            return c.symbol_name(), t, e
    return None
Example #26
0
def vlog_match_widths(left: FNode,
                      right: FNode,
                      extend=False) -> Tuple[FNode, FNode]:
    '''
    Match the bit-widths for assignment using the Verilog standard semantics.

    if extend:
       zero extend to largest width
    else:
       left_width == right_width: no change
       left_width > right_width: right side is zero extended or sign extended depending on signedness
       left_width < right_width: right side is truncated (MSBs removed)
    '''
    assert type(left) == FNode and get_type(
        left).is_bv_type(), "Expecting a bit-vector"
    assert type(right) == FNode and get_type(
        right).is_bv_type(), "Expecting a bit-vector"

    left_width, right_width = left.bv_width(), right.bv_width()

    if left_width == right_width:
        pass
    elif left_width > right_width:
        # TODO: Check signed-ness of right-side

        fun = None
        padding = 0

        # handle ops with overflow:
        if right.is_bv_add():
            fun = BVAdd
            padding = 1
        elif right.is_bv_mul():
            fun = BVMul
            padding = 1
        elif right.is_bv_lshl():
            fun = BVLShl
            padding = left_width - right_width

        assert padding >= 0, "Expecting a non-negative padding"

        # TODO: Handle signed values here as well
        # re-build the node
        if padding > 0:
            args = [BVZExt(a, padding) for a in right.args()]
            right = fun(*args)

        # re-evauluate left_width and right_width, in case they're updated
        left_width, right_width = left.bv_width(), right.bv_width()

        assert left_width >= right_width, "Unexpected bitwidth mismatch"

        if left_width > right_width:
            right = BVZExt(right, left_width - right_width)

    else:
        if extend:
            left = BVZExt(left, right_width - left_width)
        else:
            right = BVExtract(right, 0, left_width - 1)

    return simplify(left), simplify(right)
Example #27
0
def create_new_fnode_for_num(num):
    content = FNodeContent(11, (), long(num))
    node = FNode(content, 100000 + num)
    return node
Example #28
0
def mul_num_and_fnode(num, node):
    node0 = create_new_fnode_for_num(num)
    content = FNodeContent(15, (node0, node), None)
    result = FNode(content, 200000 + num)
    return result
Example #29
0
def equation_to_inequation(Formula):
    NewContent = FNodeContent(16, Formula.args(), None)
    NewFormula = FNode(NewContent, -1)
    return NewFormula
Example #30
0
def test():
    f = open("./benchmark/test4.smt2")
    Str = f.read()
    parser = SmtLibParser()
    script = parser.get_script(cStringIO(Str))
    target_formula = script.get_last_formula()
    formulas_arith = list(target_formula.get_atoms())

    equations = []
    inequations = []

    # split formulas into equations and inequations.
    for x in formulas_arith:
        if is_equation(x):
            equations.append(x.simplify())
        else:
            inequations.append(x.simplify())
    print0(equations, inequations)

    # find all let sentences (it seems that all the lets have been substituted by original terms)
    #    lets = script.filter_by_command_name("let")
    #    if len(lets) == 0:
    #        print("no lets")
    #    else:
    #        print("lets")
    #    for l in lets:
    #        print(l)

    #From now on, start step 2.
    #Construct a new question according to left inequations.
    new_question = reduce(And, inequations)
    if not is_sat(new_question):
        print("Unsat")
        analyse_unsat(new_question)
        return False
    else:
        #turn all <= into <
        new_formulas = []
        for x in inequations:
            new_formulas.append(less_than_to_less(x))
        print("*" * 25, "new_formulas", "*" * 25)
        print(new_formulas)

    #Construct a new question according to left inequations(new).
    new_questionLQ = reduce(And, new_formulas)
    if is_sat(new_questionLQ):
        #call spass and return
        pass
    else:
        unsat_core = analyse_unsat(new_questionLQ)
        for x in unsat_core:
            equations.append(inequation_to_equation(x).simplify())
            inequations.remove(find_same_formula(inequations, x))
    print0(equations, inequations)

    #From now on, start step 3
    if equations != []:
        for e in equations:
            e.simplify()
        variable_list = list(find_all_variables(equations))
        equations_coefficient = extract_coefficient_in_formulas(
            equations, variable_list)
        print("variable list is:\n %s" % (variable_list))
        print("coefficient matrix is:\n%s" % (equations_coefficient))

        # start smithify.
        coefficient_matrix = smith_nf.Matrix(equations_coefficient)
        right_matrix = extract_right_side_of_formulas(equations)
        print(type(coefficient_matrix.matrix[:][0]))
        coefficient_matrix.smithify()

        print("smith normal form is:\n%s" % (coefficient_matrix.matrix))
        print("matrix U is\n%s" % (coefficient_matrix.U))
        print("matrix V is\n%s" % (coefficient_matrix.V))
        print("right side is:\n%s" % (right_matrix))

        U_mul_right = np.dot(coefficient_matrix.U, right_matrix)
        print("U * right =\n%s" % (U_mul_right))

        # if the rank of the smithify matrix is n, then we just want first n elements of the u_mul_tight.
        trancate_index = np.linalg.matrix_rank(coefficient_matrix.matrix)
        U_mul_right_m = U_mul_right[:][:trancate_index]
        print("After truncation, U * right = \n%s" % (U_mul_right_m))

        variable_num = len(variable_list)
        new_variables_list = []
        for i in range(len(coefficient_matrix.V[0]) - trancate_index):
            new_variables_list.append(create_new_variable(i))

        transform_equations_dict = {}
        for i in range(len(coefficient_matrix.V)):
            temp0 = np.dot(coefficient_matrix.V[i][:trancate_index],
                           U_mul_right_m)
            temp0_node = create_new_fnode_for_num(temp0)
            nums_list = coefficient_matrix.V[i][trancate_index:]
            temp1 = mul_nums_and_fnodes(nums_list, new_variables_list)
            #print("temp1 is:   %s" % (temp1))
            content = FNodeContent(13, (temp0_node, temp1), None)
            temp2 = FNode(content, 400000 + i)
            #print("temp2 is:   %s" % (temp2.simplify()))
            transform_equations_dict[variable_list[i]] = temp2.simplify()

        print("the transform dictionary is:\n%s" % (transform_equations_dict))
        print(transform_equations_dict[variable_list[0]])
        print("new variables list is:")
        print(new_variables_list)
        #return transform_equations_dict
        #print(transform_equations_dict[variable_list[0].serialize()])

    else:
        # if there is no equation, solve the question by spass
        pass

    #From now on ,start step 4.
    new_inequations = []
    for f in inequations:
        new_inequations.append(
            f.substitute(transform_equations_dict).simplify())
    print("new inequations is:\n%s" % (new_inequations))

    #print(new_inequations[0].args()[0])

    #From now on, start step 5.
    #Transform equations are stored in a dictionary called transform_equations_dict, and applying this transform, we turn inequations into new_inequations.
    #In step 6, we need to construct a new script variable for this question, and return the transform dictionary and the new question.
    #Also, we need to declare the new variable in script

    new_parser = SmtLibParser()
    new_script = new_parser.get_script(cStringIO(Str))
    new_assert_command = SmtLibCommand(smtcmd.ASSERT, [And(new_inequations)])
    #print("new command is:")
    #print(type(new_command.args))
    for index, cmd in enumerate(script.commands):
        if cmd.name == smtcmd.ASSERT:
            new_script.commands[index] = new_assert_command
        if cmd.name == smtcmd.DECLARE_FUN:
            last_declare_fun = index

    for i in range(0, len(new_variables_list)):
        temp_new_declare_command = SmtLibCommand(smtcmd.DECLARE_FUN,
                                                 [new_variables_list[i]])
        new_script.commands.insert(last_declare_fun + i + 1,
                                   temp_new_declare_command)

    print("new script's content is:")
    print(new_script.commands)

    new_script.to_file("./benchmark/new_test4.smt2")