HPAC / matchpy

A library for pattern matching on symbolic expressions in Python.
MIT License
164 stars 25 forks source link

unexpected keyword argument error in `ManyToOneReplacer` #20

Closed arihantparsoya closed 7 years ago

arihantparsoya commented 7 years ago

When adding multiple rules in ManyToOneReplacer along with CustomConstraint, I recieve TypeError: <lambda>() got an unexpected keyword argument 'i2'

Code:

import matchpy
Pattern, ReplacementRule, ManyToOneReplacer = matchpy.Pattern, matchpy.ReplacementRule, matchpy.ManyToOneReplacer

from matchpy import replace_all, is_match, Wildcard, CustomConstraint
from sympy.integrals import Integral
from sympy import Symbol, Pow, cacheit, Basic
from matchpy.expressions.functions import register_operation_iterator, register_operation_factory
from matchpy import Operation, CommutativeOperation, AssociativeOperation, OneIdentityOperation

class WC(Wildcard, Symbol):
    def __init__(self, min_length, fixed_size, variable_name=None, default=None, **assumptions):
        Wildcard.__init__(self, min_length, fixed_size, variable_name, default)

    def __new__(cls, min_length, fixed_size, variable_name=None, default=None, **assumptions):
        cls._sanitize(assumptions, cls)
        return WC.__xnew__(cls, min_length, fixed_size, variable_name, default, **assumptions)

    def __getnewargs__(self):
        return (self.min_length, self.fixed_size, self.variable_name, self.optional)

    @staticmethod
    @cacheit
    def __xnew__(cls, min_length, fixed_size, variable_name=None, default=None, **assumptions):
        obj = Symbol.__xnew__(cls, variable_name, **assumptions)
        return obj

    def _hashable_content(self):
        return super()._hashable_content() + (self.min_count, self.fixed_size, self.variable_name, self.optional)

Operation.register(Integral)
register_operation_iterator(Integral, lambda a: (a._args[0],) + a._args[1], lambda a: len(a._args))

Operation.register(Pow)
OneIdentityOperation.register(Pow)
register_operation_iterator(Pow, lambda a: a._args, lambda a: len(a._args))

def sympy_op_factory(old_operation, new_operands, variable_name):
     return type(old_operation)(*new_operands)

register_operation_factory(Basic, sympy_op_factory)

a_ = WC(1, True, 'a')
m_ = WC(1, True, 'm')
x_ = WC(1, True, 'x')
x = Symbol('x')
m = Symbol('m')

subject = Integral(x**2, x)

rubi = ManyToOneReplacer()

pattern1 = Pattern(Integral(a_, x_), CustomConstraint(lambda a, x: not a.has(x)))
rule1 = ReplacementRule(pattern1, lambda x, a : Mul(a, x))
rubi.add(rule1)

pattern3 = Pattern(Integral(Pow(x_, WC(1, True, 'm')), x_), CustomConstraint(lambda m, x: not m.has(x)))
rule3 = ReplacementRule(pattern3, lambda x, m : m)
rubi.add(rule3)

print(rubi.replace(subject))

Error:

Traceback (most recent call last):
  File "r1.py", line 60, in <module>
    print(rubi.replace(subject))
  File "/Users/parsoyaarihant/anaconda/lib/python3.6/site-packages/matchpy-0.5.dev51+ge514171-py3.6.egg/matchpy/matching/many_to_one.py", line 797, in replace
TypeError: <lambda>() got an unexpected keyword argument 'i2'
arihantparsoya commented 7 years ago

Adding a single rule in ManyToOneReplacer does not raise this issue.

wheerd commented 7 years ago

The problem is that MatchPy tries to copy wildvcards when renaming them and sympy has cached symbol instances that are not actually copied. The problem is the @cacheit on the __xnew__, if you remove it it works fine.

By the way, you have to be careful with your factories for Integral. The number of elements in the iterator and the length are going to differ which might cause problems.

arihantparsoya commented 7 years ago

Thanks.