Exemple #1
0
def maybe_add_substitution(
    sub: Substitution,
    var: str,
    replacement: Expression,
) -> Optional[Substitution]:
    """Add var -> replacement to sub if possible.

    The addition is possible if, for all a -> b in sub,
    b(var -> replacement) does not contain a.

    :param sub: Existing set of substitutions
    :param var: Variable whose substitution is being defined.
    :param replacemnet: Expression that :ref:`var` might be replaced with.
    :returns: New substitutino set including var -> replacement
    or None if this isn't possible"""
    new_substitutions = {var: replacement}
    if matchpy.contains_variables_from_set(replacement, {var}):
        # print("Failed occurs check", var, "→", replacement)
        return None  # Occurs check failed
    for v, term in sub.items():
        new_term = substitute(term, Substitution({var: replacement}))
        if matchpy.contains_variables_from_set(new_term, {v}):
            print("Failed other occurs check", term, "/", v, "→", new_term,
                  "under", var, "→", replacement)
            return None  # Occurs check failed
        if new_term != term:
            new_substitutions[v] = new_term
    return Substitution(sub, **new_substitutions)
Exemple #2
0
def test_matchpy_connector():
    if matchpy is None:
        skip("matchpy not installed")

    from multiset import Multiset
    from matchpy import Pattern, Substitution

    w_ = WildDot("w_")
    w__ = WildPlus("w__")
    w___ = WildStar("w___")

    expr = x + y
    pattern = x + w_
    p, subst = _get_first_match(expr, pattern)
    assert p == Pattern(pattern)
    assert subst == Substitution({'w_': y})

    expr = x + y + z
    pattern = x + w__
    p, subst = _get_first_match(expr, pattern)
    assert p == Pattern(pattern)
    assert subst == Substitution({'w__': Multiset([y, z])})

    expr = x + y + z
    pattern = x + y + z + w___
    p, subst = _get_first_match(expr, pattern)
    assert p == Pattern(pattern)
    assert subst == Substitution({'w___': Multiset()})
Exemple #3
0
def test_matchpy_optional():
    if matchpy is None:
        skip("matchpy not installed")

    from matchpy import Pattern, Substitution
    from matchpy import ManyToOneReplacer, ReplacementRule

    p = WildDot("p", optional=1)
    q = WildDot("q", optional=0)

    pattern = p*x + q

    expr1 = 2*x
    pa, subst = _get_first_match(expr1, pattern)
    assert pa == Pattern(pattern)
    assert subst == Substitution({'p': 2, 'q': 0})

    expr2 = x + 3
    pa, subst = _get_first_match(expr2, pattern)
    assert pa == Pattern(pattern)
    assert subst == Substitution({'p': 1, 'q': 3})

    expr3 = x
    pa, subst = _get_first_match(expr3, pattern)
    assert pa == Pattern(pattern)
    assert subst == Substitution({'p': 1, 'q': 0})

    expr4 = x*y + z
    pa, subst = _get_first_match(expr4, pattern)
    assert pa == Pattern(pattern)
    assert subst == Substitution({'p': y, 'q': z})

    replacer = ManyToOneReplacer()
    replacer.add(ReplacementRule(Pattern(pattern), lambda p, q: sin(p)*cos(q)))
    assert replacer.replace(expr1) == sin(2)*cos(0)
    assert replacer.replace(expr2) == sin(1)*cos(3)
    assert replacer.replace(expr3) == sin(1)*cos(0)
    assert replacer.replace(expr4) == sin(y)*cos(z)
Exemple #4
0
def unify_expressions(left: Expression,
                      right: Expression) -> List[Substitution]:
    """Return a substitution alpha such that
    :ref:`left` * alpha == :ref:`right` * alpha,
    or None if none such exists.

    For best results, the expressions should not share variables.
    This function does not ensure that

    :param left: An expression to unify.
    :param right: An expression to unify
    :returns: The unifying substitution, or None"""
    main_ret = []

    root_ret = Substitution()
    root_to_operate = deque([(left, right)])
    operations = [(root_ret, root_to_operate)]
    while operations:
        preserve_this = True
        ret, to_operate = operations.pop()
        # print("Trace:","Have", ret, "processing", ", ".join(map(lambda x: str((str(x[0]), str(x[1]))), to_operate)))
        if not to_operate:  # Successful unification
            main_ret.append(ret)
            continue

        t1, t2 = to_operate.popleft()
        # print("Try to unify", t1, "and", t2)
        if t1 == t2:
            operations.append((ret, to_operate))
            # print("Equality continue")
            continue

        any_change = False
        if isinstance(t1, Wildcard):
            new_subs = maybe_add_substitution(ret, t1.variable_name, t2)
            if new_subs is not None:
                ret = new_subs
                any_change = True
            else:
                continue  # Here we drop the branch
        elif isinstance(t2, Wildcard):
            new_subs = maybe_add_substitution(ret, t2.variable_name, t1)
            if new_subs is not None:
                ret = new_subs
                any_change = True
            else:
                continue
        elif (get_head(t1) == get_head(t2) and isinstance(t1, Operation)
              and isinstance(t2, Operation)):
            # Unify within functions
            if t1.associative and t1.commutative:
                potential_unifiers = ac_operand_lists(t1, t2)
                preserve_this = False
                for i in potential_unifiers:
                    new_ret = copy(ret)
                    new_to_operate = copy(to_operate)
                    new_to_operate.extend(i)
                    operations.append((new_ret, new_to_operate))
            elif t1.associative or t1.commutative:
                raise (NotImplementedError(
                    "Straight associative or commutative ain't happening")
                       )  # noqa
            elif len(t1.operands) == len(t2.operands):
                to_operate.extend((zip(t1.operands, t2.operands)))
            else:
                continue
        else:
            continue

        if any_change:
            new_queue = deque()  # type: Deque[Tuple[Expression, Expression]]
            while to_operate:
                a, b = to_operate.popleft()
                new_a = substitute(a, ret)
                new_b = substitute(b, ret)
                if a != new_a:
                    pass  # print("Substituion modified", a, "→", new_a)
                if b != new_b:
                    pass  # print("Substituion modified", b, "→", new_b)
                new_queue.append((new_a, new_b))
            to_operate = new_queue

        if preserve_this:
            operations.append((ret, to_operate))

    return main_ret