Package: matchpy
MatchPy is a library for pattern matching on symbolic expressions in Python.Work in progressInstallationMatchPy is available viaPyPI, and for Conda viaconda-forge. It can be installed withpip install matchpyorconda install-cconda-forgematchpy.OverviewThis package implementspattern matchingin Python. Pattern matching is a powerful tool for symbolic computations, operating on symbolic expressions. Given a pattern and an expression (which is usually calledsubject), the goal of pattern matching is to find a substitution for all the variables in the pattern such that the pattern becomes the subject. As an example, consider the patternf(x), wherefis a function andxis a variable, and the subjectf(a), whereais a constant symbol. Then the substitution that replacesxwithais a match. MatchPy supports associative and/or commutative function symbols, as well as sequence variables, similar to pattern matching inMathematica.A detailed example of how to use MatchPy can be foundhere.MatchPy supports both one-to-one and many-to-one pattern matching. The latter makes use of similarities between patterns to efficiently find matches for multiple patterns at the same time.A list of publications about MatchPy can be foundbelow.ExpressionsExpressions are tree-like data structures, consisting of operations (functions, internal nodes) and symbols (constants, leaves):>>> from matchpy import Operation, Symbol, Arity
>>> f = Operation.new('f', Arity.binary)
>>> a = Symbol('a')
>>> print(f(a, a))
f(a, a)Patterns are expressions which may contain wildcards (variables):>>> from matchpy import Pattern, Wildcard
>>> x = Wildcard.dot('x')
>>> print(Pattern(f(a, x)))
f(a, x_)In the previous example,xis the name of the variable. However, it is also possible to use wildcards without names:>>> w = Wildcard.dot()
>>> print(Pattern(f(w, w)))
f(_, _)It is also possible to assign variable names to entire subexpressions:>>> print(Pattern(f(w, a, variable_name='y')))
y: f(_, a)Pattern MatchingGiven a pattern and an expression (which is usually called subject), the idea of pattern matching is to find a substitution that maps wildcards to expressions such that the pattern becomes the subject. In MatchPy, a substitution is a dict that maps variable names to expressions.>>> from matchpy import match
>>> y = Wildcard.dot('y')
>>> b = Symbol('b')
>>> subject = f(a, b)
>>> pattern = Pattern(f(x, y))
>>> substitution = next(match(subject, pattern))
>>> print(substitution)
{x ↦ a, y ↦ b}Applying the substitution to the pattern results in the original expression.>>> from matchpy import substitute
>>> print(substitute(pattern, substitution))
f(a, b)Sequence WildcardsSequence wildcards are wildcards that can match a sequence of expressions instead of just a single expression:>>> z = Wildcard.plus('z')
>>> pattern = Pattern(f(z))
>>> subject = f(a, b)
>>> substitution = next(match(subject, pattern))
>>> print(substitution)
{z ↦ (a, b)}Associativity and CommutativityMatchPy natively supports associative and/or commutative operations. Nested associative operators are automatically flattened, the operands in commutative operations are sorted:>>> g = Operation.new('g', Arity.polyadic, associative=True, commutative=True)
>>> print(g(a, g(b, a)))
g(a, a, b)Associativity and commutativity is also considered for pattern matching:>>> pattern = Pattern(g(b, x))
>>> subject = g(a, a, b)
>>> print(next(match(subject, pattern)))
{x ↦ g(a, a)}
>>> h = Operation.new('h', Arity.polyadic)
>>> pattern = Pattern(h(b, x))
>>> subject = h(a, a, b)
>>> list(match(subject, pattern))
[]Many-to-One MatchingWhen a fixed set of patterns is matched repeatedly against different subjects, matching can be sped up significantly by using many-to-one matching. The idea of many-to-one matching is to construct a so called discrimination net, a data structure similar to a decision tree or a finite automaton that exploits similarities between patterns. In MatchPy, there are two such data structures, implemented as classes:DiscriminationNetandManyToOneMatcher. The DiscriminationNet class only supports syntactic pattern matching, that is, operations are neither associative nor commutative. Sequence variables are not supported either. The ManyToOneMatcher class supports associative and/or commutative matching with sequence variables. For syntactic pattern matching, the DiscriminationNet should be used, as it is usually faster.>>> pattern1 = Pattern(f(a, x))
>>> pattern2 = Pattern(f(y, b))
>>> matcher = ManyToOneMatcher(pattern1, pattern2)
>>> subject = f(a, b)
>>> matches = matcher.match(subject)
>>> for matched_pattern, substitution in sorted(map(lambda m: (str(m[0]), str(m[1])), matches)):
...     print('{} matched with {}'.format(matched_pattern, substitution))
f(a, x_) matched with {x ↦ b}
f(y_, b) matched with {y ↦ a}RoadmapBesides the existing features, we plan on adding the following to MatchPy:Support for Mathematica’sAlternatives: For examplef(a | b)would match eitherf(a)orf(b).Support for Mathematica’sRepeated: For examplef(a..)would matchf(a),f(a, a),f(a, a, a), etc.Support pattern sequences (PatternSequencein Mathematica). These are mainly useful in combination withAlternativesorRepeated, e.g.f(a | (b, c))would match eitherf(a)orf(b, c).f((aa)..)would match anyfwith an even number ofaarguments.All these additional pattern features need to be supported in theManyToOneMatcheras well.Better integration with existing types such asdict.Code generation for both one-to-one and many-to-one matching. There is already an experimental implementation, but it still has some dependencies on MatchPy which can probably be removed.Improving the documentation with more examples.Better test coverage with more randomized tests.Implementation of the matching algorithms in a lower-level language, for example C, both for performance and to make MatchPy’s functionality available in other languages.ContributingIf you have some issue or want to contribute, please feel free to open an issue or create a pull request. Help is always appreciated!The Makefile has several tasks to help development:To install all needed packages, you can usemake init.To run the tests you can usemake test. The tests usepytest.To generate the documentation you can usemake docs.To run the style checker (pylint) you can usemake check.If you have any questions or need help with setting things up, please open an issue and we will try the best to assist you.PublicationsMatchPy: Pattern Matching in PythonManuel Krebber and Henrik BarthelsJournal of Open Source Software, Volume 3(26), pp. 2, June 2018.Efficient Pattern Matching in PythonManuel Krebber, Henrik Barthels and Paolo BientinesiProceedings of the 7th Workshop on Python for High-Performance and Scientific Computing, November 2017.MatchPy: A Pattern Matching LibraryManuel Krebber, Henrik Barthels and Paolo BientinesiProceedings of the 15th Python in Science Conference, July 2017.Non-linear Associative-Commutative Many-to-One Pattern Matching with Sequence VariablesManuel KrebberMaster Thesis, RWTH Aachen University, May 2017If you want to cite MatchPy, please reference the JOSS paper:@article{krebber2018,
    author    = {Manuel Krebber and Henrik Barthels},
    title     = {{M}atch{P}y: {P}attern {M}atching in {P}ython},
    journal   = {Journal of Open Source Software},
    year      = 2018,
    pages     = 2,
    month     = jun,
    volume    = {3},
    number    = {26},
    doi       = "10.21105/joss.00670",
    web       = "http://joss.theoj.org/papers/10.21105/joss.00670",
}