예제 #1
0
def _eliminate_biconditional(node: syntax.Node, *args) -> syntax.Node:
    """Eliminates biconditional.

    :param node: The node to rewrite.
    :returns: Rewritten node.

    Rewrites expressions of type `A <=> B` into `(A => B) & (B => A)`.
    """

    if not node.is_equivalence():
        return node

    a, b = node.children
    child1 = syntax.make_formula(syntax.IMPLICATION, [a, b])
    child2 = syntax.make_formula(syntax.IMPLICATION, [b, a])
    children = [child1, child2]
    return syntax.make_formula(syntax.CONJUNCTION, children)
예제 #2
0
def _standardize_quantified_variables(node: syntax.Node,
                                      state: syntax.WalkState) -> syntax.Node:
    """Standardizes quantified variables by giving them unique names.

    :param node: The node to rewrite.
    :param state: Rewriting state.
    :returns: Rewritten node.

    Rewrites expressions of type:

    - `*x: A(x)` into `*var_1: A(var_1)`,
    - `?x: A(x)` into `?var_1: A(var_1)`.
    """

    seen: List[syntax.Node] = state.context.setdefault('seen', [])
    replaced: syntax.T_Substitution = state.context.setdefault('replaced', {})

    # todo: replaced would not work with nested quantified formulas that
    #  reuse the same symbol, but actually never mind - it's a corner case
    #  I don't want to fiddle with now

    if node in seen:
        return node

    if node.is_quantified():
        old = node.get_quantified_variable().value
        if old.startswith('_'):
            return node  # already renamed

        new = _new_variable_name()
        qtype = node.get_quantifier_type()
        quant = syntax.make_quantifier(qtype, new)
        rv = syntax.make_formula(quant, node.children)

        replaced[new] = node.get_quantified_variable()
        state.stack.append((old, new))
        seen.append(rv)
        return rv

    elif node.is_variable():
        # reversed, because we want to rename symbol to the last seen value.
        # Example: We want to rewrite `?x, ?x: x` into `?a: ?b: b`.
        for old, new in reversed(state.stack):
            if old == node.value:
                rv = syntax.make_variable(new)
                seen.append(rv)
                return rv

        return node

    else:
        return node
예제 #3
0
def _distribute_conjunction(node: syntax.Node, *args) -> syntax.Node:
    """Distributes conjunctions over disjunctions.

    :param node: The node to rewrite.
    :returns: Rewritten node.

    Rewrites expressions of type `(A & B) | C` into `(A | C) & (B | C)`.
    """

    if not node.is_disjunction():
        return node

    for child in node.children:
        other = next(x for x in node.children if x is not child)
        if child.is_conjunction():
            a, b = child.children
            rv1 = syntax.make_formula(syntax.DISJUNCTION, [a, other])
            rv2 = syntax.make_formula(syntax.DISJUNCTION, [b, other])
            rv = syntax.make_formula(syntax.CONJUNCTION, [rv1, rv2])
            return rv

    return node
예제 #4
0
def _eliminate_implication(node: syntax.Node, *args) -> syntax.Node:
    """Eliminates implication.

    :param node: The node to rewrite.
    :returns: Rewritten node.

    Rewrites expressions of type `A => B` into `!A | B`.
    """

    if not node.is_implication():
        return node

    a, b = node.children
    children = [a.negate(), b]
    return syntax.make_formula(syntax.DISJUNCTION, children)
예제 #5
0
def _propagate_negation(node: syntax.Node, *args) -> syntax.Node:
    """Propagates negations down the syntax tree.

    :param node: The node to rewrite.
    :returns: Rewritten node.

    Rewrites expressions of type:

    - `!!A` into `A`,
    - `!(A & B)` into `!A | !B`,
    - `!(A | B)` into `!A & !B`,
    - `!(*x: A)` into `?x: !A`,`
    - `!(?x: A)` into `*x: !A`.
    """

    if not node.is_negation():
        return node

    rv = None
    child = node.children[0]

    # Double negation
    if child.is_negation():
        return child.children[0]

    # De Morgan
    elif child.is_conjunction():
        rv = syntax.DISJUNCTION

    # De Morgan
    elif child.is_disjunction():
        rv = syntax.CONJUNCTION

    # Flip Quantifiers
    elif child.is_quantified():
        qtype = next(
            k for k in
            [syntax.UNIVERSAL_QUANTIFIER, syntax.EXISTENTIAL_QUANTIFIER]
            if k != child.get_quantifier_type())
        qname = child.get_quantified_variable().value
        rv = syntax.make_quantifier(qtype, qname)

    if rv is None:
        return node
    else:
        children = [x.negate() for x in child.children]
        return syntax.make_formula(rv, children)