sympy / sympy

A computer algebra system written in pure Python
https://sympy.org/
Other
13k stars 4.44k forks source link

sympy and structural pattern matching (PEP 622) #21193

Closed fakuivan closed 3 years ago

fakuivan commented 3 years ago

Structural Pattern Matching is being planned for 3.10. From this I gathered that pattern matching is something that would be nice to have. In my case I would like to be able to use this to implement a more complete algorithm for computing laplace transforms. Are there any plans or thoughts on how this feature could be used while developing and using sympy?

oscarbenjamin commented 3 years ago

For SymPy itself the codebase could not use structural pattern matching until after dropping support for Python 3.9.

Some SymPy types could grow a __match_args__ class attribute to facilitate matching like case Pow(x, 2): in downstream code. The mechanism in the currently accepted PEP does not allow for user types with variadic args to be matched directly with constructor syntax so for those it would have to be something like case Mul(args=(2, x)):.

asmeurer commented 3 years ago

I haven't looked too closely at it yet, but it did seem like the extension possibilities were rather limited in a way that would make it hard to use with SymPy. The class matching seems to be based on attributes, meaning as Oscar said it would likely have to be case Mul(args=(2, x)). Also any variable in the pattern is treated like a wildcard (so case Mul(args=(2, x)) would match 2*x or 2*y). To match x as a specific variable, if I understand the PEP correctly, you'd have to spell it out like Mul(args=(2, Symbol('x')). You can get around this by using attributes (see https://stackoverflow.com/questions/66159432/match-statement-how-to-use-values-stored-in-variables). Maybe we can build something out of this, but I get the feeling that a SymPy level pattern matcher will be more useful than using the Python one. I just tested CPython master and case 2*x is a syntax error.

It also doesn't really seem to be possible to extend the matching itself, from what I can tell. So you can't make things match unless they look exactly like they are supposed to, which isn't particularly useful. We need a pattern like x*sin(y) to match even if the resulting expression is actually sin(y)*x. Mul(args=(x, sin(y)) already won't work for this.

(as I said I haven't read the PEP super closely yet, so if any of this is wrong, please correct me)

MatchPy can easily handle this sort of thing (commutative/associative matching), and we can build a DSL on top of it to make the matching expressions be written just like SymPy expressions.

At best, the pattern matching syntax can replace code in SymPy that dispatches based on type (case Mul, case Add). This is not uncommon, so I'm sure it will be useful. But I am very doubtful it can be used to do "pattern matching" the way one would commonly think of it in a CAS.

oscarbenjamin commented 3 years ago

Note that while it might seem that pattern matching is a good fit for SymPy the way it was designed for Python doesn't quite work for SymPy's design. I did feedback about this before the pattern matching PEP was accepted: https://www.mail-archive.com/python-dev@python.org/msg109349.html

oscarbenjamin commented 3 years ago

I don't think we need to keep an issue open for this.