Skip to content

Implement in-house unification#2120

Draft
ricardoV94 wants to merge 2 commits into
pymc-devs:mainfrom
ricardoV94:vendor_unify
Draft

Implement in-house unification#2120
ricardoV94 wants to merge 2 commits into
pymc-devs:mainfrom
ricardoV94:vendor_unify

Conversation

@ricardoV94
Copy link
Copy Markdown
Member

@ricardoV94 ricardoV94 commented May 11, 2026

  • Replace unification-based PatternNodeRewriter with specialized matcher
  • Add variadic Asterisk and auto-detected commutative match

Second commit serves to illustrate the advantages of implementing our own unification machinery.

This should also facilitate integration with equality-saturation rewrites.

Also performance:

case unification.unify (old) match_pattern (new) speedup
shallow_match (log, (exp, "x")) ✓ 99.9 µs 7.2 µs 14×
deep_match (log, (exp, (log, (exp, "x")))) ✓ 195.2 µs 12.8 µs 15×
repeated_var (mul, "x", "x") ✓ 65.6 µs 4.4 µs 15×
with_constant (sub, 1.0, (erf, "x")) ✓ 137.8 µs 4.9 µs 28×
no_match (log, (exp, "x")) vs log(x) ✗ 60.7 µs 3.9 µs 15×

And less dependencies!

Closes #1609

Comment on lines +293 to +297
def _is_commutative_op(op) -> bool:
if getattr(op, "commutative", False):
return True
scalar_op = getattr(op, "scalar_op", None)
return bool(getattr(scalar_op, "commutative", False))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this info to Elemwise (it can borrow it from the core op)

@ricardoV94 ricardoV94 force-pushed the vendor_unify branch 2 times, most recently from c268345 to 29d65cf Compare May 11, 2026 13:59
Drops logical-unification, kanren, etuples and cons as required deps;
they move to the `pytensor[kanren]` extra (still pulled in by
`pytensor[tests]`). Importing pytensor.graph.rewriting.kanren registers
PatternVar via Var.register so external isinstance(x, unification.Var) /
isvar(x) still hold. 10-20x faster match in microbenchmarks.
Commutativity is detected from op.commutative (or scalar_op.commutative
for Elemwise). The matcher tries the original input order in place
first; only on failure does it copy subs and backtrack through
permutations. Asterisk preserves input order in both paths.
@ricardoV94
Copy link
Copy Markdown
Member Author

One reason I want this is that right now I'm weary of using PatternNodeRewriter rewrites even in cases where it's much cleaner to express like this because of poor performance. Eg noted a missing sqr(abs(x)) -> sqr(x) missing in pymc-devs/pymc#8297

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reconsider using unification library for PatternNodeRewriter

1 participant